1/* 2Ginkgo is a BDD-style testing framework for Golang 3 4The godoc documentation describes Ginkgo's API. More comprehensive documentation (with examples!) is available at http://onsi.github.io/ginkgo/ 5 6Ginkgo's preferred matcher library is [Gomega](http://github.com/onsi/gomega) 7 8Ginkgo on Github: http://github.com/onsi/ginkgo 9 10Ginkgo is MIT-Licensed 11*/ 12package ginkgo 13 14import ( 15 "flag" 16 "fmt" 17 "io" 18 "net/http" 19 "os" 20 "strings" 21 "time" 22 23 "github.com/onsi/ginkgo/config" 24 "github.com/onsi/ginkgo/internal/codelocation" 25 "github.com/onsi/ginkgo/internal/failer" 26 "github.com/onsi/ginkgo/internal/remote" 27 "github.com/onsi/ginkgo/internal/suite" 28 "github.com/onsi/ginkgo/internal/testingtproxy" 29 "github.com/onsi/ginkgo/internal/writer" 30 "github.com/onsi/ginkgo/reporters" 31 "github.com/onsi/ginkgo/reporters/stenographer" 32 colorable "github.com/onsi/ginkgo/reporters/stenographer/support/go-colorable" 33 "github.com/onsi/ginkgo/types" 34) 35 36const GINKGO_VERSION = config.VERSION 37const GINKGO_PANIC = ` 38Your test failed. 39Ginkgo panics to prevent subsequent assertions from running. 40Normally Ginkgo rescues this panic so you shouldn't see it. 41 42But, if you make an assertion in a goroutine, Ginkgo can't capture the panic. 43To circumvent this, you should call 44 45 defer GinkgoRecover() 46 47at the top of the goroutine that caused this panic. 48` 49const defaultTimeout = 1 50 51var globalSuite *suite.Suite 52var globalFailer *failer.Failer 53 54func init() { 55 config.Flags(flag.CommandLine, "ginkgo", true) 56 GinkgoWriter = writer.New(os.Stdout) 57 globalFailer = failer.New() 58 globalSuite = suite.New(globalFailer) 59} 60 61//GinkgoWriter implements an io.Writer 62//When running in verbose mode any writes to GinkgoWriter will be immediately printed 63//to stdout. Otherwise, GinkgoWriter will buffer any writes produced during the current test and flush them to screen 64//only if the current test fails. 65var GinkgoWriter io.Writer 66 67//The interface by which Ginkgo receives *testing.T 68type GinkgoTestingT interface { 69 Fail() 70} 71 72//GinkgoRandomSeed returns the seed used to randomize spec execution order. It is 73//useful for seeding your own pseudorandom number generators (PRNGs) to ensure 74//consistent executions from run to run, where your tests contain variability (for 75//example, when selecting random test data). 76func GinkgoRandomSeed() int64 { 77 return config.GinkgoConfig.RandomSeed 78} 79 80//GinkgoParallelNode returns the parallel node number for the current ginkgo process 81//The node number is 1-indexed 82func GinkgoParallelNode() int { 83 return config.GinkgoConfig.ParallelNode 84} 85 86//Some matcher libraries or legacy codebases require a *testing.T 87//GinkgoT implements an interface analogous to *testing.T and can be used if 88//the library in question accepts *testing.T through an interface 89// 90// For example, with testify: 91// assert.Equal(GinkgoT(), 123, 123, "they should be equal") 92// 93// Or with gomock: 94// gomock.NewController(GinkgoT()) 95// 96// GinkgoT() takes an optional offset argument that can be used to get the 97// correct line number associated with the failure. 98func GinkgoT(optionalOffset ...int) GinkgoTInterface { 99 offset := 3 100 if len(optionalOffset) > 0 { 101 offset = optionalOffset[0] 102 } 103 return testingtproxy.New(GinkgoWriter, Fail, offset) 104} 105 106//The interface returned by GinkgoT(). This covers most of the methods 107//in the testing package's T. 108type GinkgoTInterface interface { 109 Fail() 110 Error(args ...interface{}) 111 Errorf(format string, args ...interface{}) 112 FailNow() 113 Fatal(args ...interface{}) 114 Fatalf(format string, args ...interface{}) 115 Log(args ...interface{}) 116 Logf(format string, args ...interface{}) 117 Failed() bool 118 Parallel() 119 Skip(args ...interface{}) 120 Skipf(format string, args ...interface{}) 121 SkipNow() 122 Skipped() bool 123} 124 125//Custom Ginkgo test reporters must implement the Reporter interface. 126// 127//The custom reporter is passed in a SuiteSummary when the suite begins and ends, 128//and a SpecSummary just before a spec begins and just after a spec ends 129type Reporter reporters.Reporter 130 131//Asynchronous specs are given a channel of the Done type. You must close or write to the channel 132//to tell Ginkgo that your async test is done. 133type Done chan<- interface{} 134 135//GinkgoTestDescription represents the information about the current running test returned by CurrentGinkgoTestDescription 136// FullTestText: a concatenation of ComponentTexts and the TestText 137// ComponentTexts: a list of all texts for the Describes & Contexts leading up to the current test 138// TestText: the text in the actual It or Measure node 139// IsMeasurement: true if the current test is a measurement 140// FileName: the name of the file containing the current test 141// LineNumber: the line number for the current test 142// Failed: if the current test has failed, this will be true (useful in an AfterEach) 143type GinkgoTestDescription struct { 144 FullTestText string 145 ComponentTexts []string 146 TestText string 147 148 IsMeasurement bool 149 150 FileName string 151 LineNumber int 152 153 Failed bool 154 Duration time.Duration 155} 156 157//CurrentGinkgoTestDescripton returns information about the current running test. 158func CurrentGinkgoTestDescription() GinkgoTestDescription { 159 summary, ok := globalSuite.CurrentRunningSpecSummary() 160 if !ok { 161 return GinkgoTestDescription{} 162 } 163 164 subjectCodeLocation := summary.ComponentCodeLocations[len(summary.ComponentCodeLocations)-1] 165 166 return GinkgoTestDescription{ 167 ComponentTexts: summary.ComponentTexts[1:], 168 FullTestText: strings.Join(summary.ComponentTexts[1:], " "), 169 TestText: summary.ComponentTexts[len(summary.ComponentTexts)-1], 170 IsMeasurement: summary.IsMeasurement, 171 FileName: subjectCodeLocation.FileName, 172 LineNumber: subjectCodeLocation.LineNumber, 173 Failed: summary.HasFailureState(), 174 Duration: summary.RunTime, 175 } 176} 177 178//Measurement tests receive a Benchmarker. 179// 180//You use the Time() function to time how long the passed in body function takes to run 181//You use the RecordValue() function to track arbitrary numerical measurements. 182//The RecordValueWithPrecision() function can be used alternatively to provide the unit 183//and resolution of the numeric measurement. 184//The optional info argument is passed to the test reporter and can be used to 185// provide the measurement data to a custom reporter with context. 186// 187//See http://onsi.github.io/ginkgo/#benchmark_tests for more details 188type Benchmarker interface { 189 Time(name string, body func(), info ...interface{}) (elapsedTime time.Duration) 190 RecordValue(name string, value float64, info ...interface{}) 191 RecordValueWithPrecision(name string, value float64, units string, precision int, info ...interface{}) 192} 193 194//RunSpecs is the entry point for the Ginkgo test runner. 195//You must call this within a Golang testing TestX(t *testing.T) function. 196// 197//To bootstrap a test suite you can use the Ginkgo CLI: 198// 199// ginkgo bootstrap 200func RunSpecs(t GinkgoTestingT, description string) bool { 201 specReporters := []Reporter{buildDefaultReporter()} 202 return RunSpecsWithCustomReporters(t, description, specReporters) 203} 204 205//To run your tests with Ginkgo's default reporter and your custom reporter(s), replace 206//RunSpecs() with this method. 207func RunSpecsWithDefaultAndCustomReporters(t GinkgoTestingT, description string, specReporters []Reporter) bool { 208 specReporters = append(specReporters, buildDefaultReporter()) 209 return RunSpecsWithCustomReporters(t, description, specReporters) 210} 211 212//To run your tests with your custom reporter(s) (and *not* Ginkgo's default reporter), replace 213//RunSpecs() with this method. Note that parallel tests will not work correctly without the default reporter 214func RunSpecsWithCustomReporters(t GinkgoTestingT, description string, specReporters []Reporter) bool { 215 writer := GinkgoWriter.(*writer.Writer) 216 writer.SetStream(config.DefaultReporterConfig.Verbose) 217 reporters := make([]reporters.Reporter, len(specReporters)) 218 for i, reporter := range specReporters { 219 reporters[i] = reporter 220 } 221 passed, hasFocusedTests := globalSuite.Run(t, description, reporters, writer, config.GinkgoConfig) 222 if passed && hasFocusedTests && strings.TrimSpace(os.Getenv("GINKGO_EDITOR_INTEGRATION")) == "" { 223 fmt.Println("PASS | FOCUSED") 224 os.Exit(types.GINKGO_FOCUS_EXIT_CODE) 225 } 226 return passed 227} 228 229func buildDefaultReporter() Reporter { 230 remoteReportingServer := config.GinkgoConfig.StreamHost 231 if remoteReportingServer == "" { 232 stenographer := stenographer.New(!config.DefaultReporterConfig.NoColor, config.GinkgoConfig.FlakeAttempts > 1, colorable.NewColorableStdout()) 233 return reporters.NewDefaultReporter(config.DefaultReporterConfig, stenographer) 234 } else { 235 debugFile := "" 236 if config.GinkgoConfig.DebugParallel { 237 debugFile = fmt.Sprintf("ginkgo-node-%d.log", config.GinkgoConfig.ParallelNode) 238 } 239 return remote.NewForwardingReporter(config.DefaultReporterConfig, remoteReportingServer, &http.Client{}, remote.NewOutputInterceptor(), GinkgoWriter.(*writer.Writer), debugFile) 240 } 241} 242 243//Skip notifies Ginkgo that the current spec was skipped. 244func Skip(message string, callerSkip ...int) { 245 skip := 0 246 if len(callerSkip) > 0 { 247 skip = callerSkip[0] 248 } 249 250 globalFailer.Skip(message, codelocation.New(skip+1)) 251 panic(GINKGO_PANIC) 252} 253 254//Fail notifies Ginkgo that the current spec has failed. (Gomega will call Fail for you automatically when an assertion fails.) 255func Fail(message string, callerSkip ...int) { 256 skip := 0 257 if len(callerSkip) > 0 { 258 skip = callerSkip[0] 259 } 260 261 globalFailer.Fail(message, codelocation.New(skip+1)) 262 panic(GINKGO_PANIC) 263} 264 265//GinkgoRecover should be deferred at the top of any spawned goroutine that (may) call `Fail` 266//Since Gomega assertions call fail, you should throw a `defer GinkgoRecover()` at the top of any goroutine that 267//calls out to Gomega 268// 269//Here's why: Ginkgo's `Fail` method records the failure and then panics to prevent 270//further assertions from running. This panic must be recovered. Ginkgo does this for you 271//if the panic originates in a Ginkgo node (an It, BeforeEach, etc...) 272// 273//Unfortunately, if a panic originates on a goroutine *launched* from one of these nodes there's no 274//way for Ginkgo to rescue the panic. To do this, you must remember to `defer GinkgoRecover()` at the top of such a goroutine. 275func GinkgoRecover() { 276 e := recover() 277 if e != nil { 278 globalFailer.Panic(codelocation.New(1), e) 279 } 280} 281 282//Describe blocks allow you to organize your specs. A Describe block can contain any number of 283//BeforeEach, AfterEach, JustBeforeEach, It, and Measurement blocks. 284// 285//In addition you can nest Describe, Context and When blocks. Describe, Context and When blocks are functionally 286//equivalent. The difference is purely semantic -- you typical Describe the behavior of an object 287//or method and, within that Describe, outline a number of Contexts and Whens. 288func Describe(text string, body func()) bool { 289 globalSuite.PushContainerNode(text, body, types.FlagTypeNone, codelocation.New(1)) 290 return true 291} 292 293//You can focus the tests within a describe block using FDescribe 294func FDescribe(text string, body func()) bool { 295 globalSuite.PushContainerNode(text, body, types.FlagTypeFocused, codelocation.New(1)) 296 return true 297} 298 299//You can mark the tests within a describe block as pending using PDescribe 300func PDescribe(text string, body func()) bool { 301 globalSuite.PushContainerNode(text, body, types.FlagTypePending, codelocation.New(1)) 302 return true 303} 304 305//You can mark the tests within a describe block as pending using XDescribe 306func XDescribe(text string, body func()) bool { 307 globalSuite.PushContainerNode(text, body, types.FlagTypePending, codelocation.New(1)) 308 return true 309} 310 311//Context blocks allow you to organize your specs. A Context block can contain any number of 312//BeforeEach, AfterEach, JustBeforeEach, It, and Measurement blocks. 313// 314//In addition you can nest Describe, Context and When blocks. Describe, Context and When blocks are functionally 315//equivalent. The difference is purely semantic -- you typical Describe the behavior of an object 316//or method and, within that Describe, outline a number of Contexts and Whens. 317func Context(text string, body func()) bool { 318 globalSuite.PushContainerNode(text, body, types.FlagTypeNone, codelocation.New(1)) 319 return true 320} 321 322//You can focus the tests within a describe block using FContext 323func FContext(text string, body func()) bool { 324 globalSuite.PushContainerNode(text, body, types.FlagTypeFocused, codelocation.New(1)) 325 return true 326} 327 328//You can mark the tests within a describe block as pending using PContext 329func PContext(text string, body func()) bool { 330 globalSuite.PushContainerNode(text, body, types.FlagTypePending, codelocation.New(1)) 331 return true 332} 333 334//You can mark the tests within a describe block as pending using XContext 335func XContext(text string, body func()) bool { 336 globalSuite.PushContainerNode(text, body, types.FlagTypePending, codelocation.New(1)) 337 return true 338} 339 340//When blocks allow you to organize your specs. A When block can contain any number of 341//BeforeEach, AfterEach, JustBeforeEach, It, and Measurement blocks. 342// 343//In addition you can nest Describe, Context and When blocks. Describe, Context and When blocks are functionally 344//equivalent. The difference is purely semantic -- you typical Describe the behavior of an object 345//or method and, within that Describe, outline a number of Contexts and Whens. 346func When(text string, body func()) bool { 347 globalSuite.PushContainerNode("when "+text, body, types.FlagTypeNone, codelocation.New(1)) 348 return true 349} 350 351//You can focus the tests within a describe block using FWhen 352func FWhen(text string, body func()) bool { 353 globalSuite.PushContainerNode("when "+text, body, types.FlagTypeFocused, codelocation.New(1)) 354 return true 355} 356 357//You can mark the tests within a describe block as pending using PWhen 358func PWhen(text string, body func()) bool { 359 globalSuite.PushContainerNode("when "+text, body, types.FlagTypePending, codelocation.New(1)) 360 return true 361} 362 363//You can mark the tests within a describe block as pending using XWhen 364func XWhen(text string, body func()) bool { 365 globalSuite.PushContainerNode("when "+text, body, types.FlagTypePending, codelocation.New(1)) 366 return true 367} 368 369//It blocks contain your test code and assertions. You cannot nest any other Ginkgo blocks 370//within an It block. 371// 372//Ginkgo will normally run It blocks synchronously. To perform asynchronous tests, pass a 373//function that accepts a Done channel. When you do this, you can also provide an optional timeout. 374func It(text string, body interface{}, timeout ...float64) bool { 375 globalSuite.PushItNode(text, body, types.FlagTypeNone, codelocation.New(1), parseTimeout(timeout...)) 376 return true 377} 378 379//You can focus individual Its using FIt 380func FIt(text string, body interface{}, timeout ...float64) bool { 381 globalSuite.PushItNode(text, body, types.FlagTypeFocused, codelocation.New(1), parseTimeout(timeout...)) 382 return true 383} 384 385//You can mark Its as pending using PIt 386func PIt(text string, _ ...interface{}) bool { 387 globalSuite.PushItNode(text, func() {}, types.FlagTypePending, codelocation.New(1), 0) 388 return true 389} 390 391//You can mark Its as pending using XIt 392func XIt(text string, _ ...interface{}) bool { 393 globalSuite.PushItNode(text, func() {}, types.FlagTypePending, codelocation.New(1), 0) 394 return true 395} 396 397//Specify blocks are aliases for It blocks and allow for more natural wording in situations 398//which "It" does not fit into a natural sentence flow. All the same protocols apply for Specify blocks 399//which apply to It blocks. 400func Specify(text string, body interface{}, timeout ...float64) bool { 401 globalSuite.PushItNode(text, body, types.FlagTypeNone, codelocation.New(1), parseTimeout(timeout...)) 402 return true 403} 404 405//You can focus individual Specifys using FSpecify 406func FSpecify(text string, body interface{}, timeout ...float64) bool { 407 globalSuite.PushItNode(text, body, types.FlagTypeFocused, codelocation.New(1), parseTimeout(timeout...)) 408 return true 409} 410 411//You can mark Specifys as pending using PSpecify 412func PSpecify(text string, is ...interface{}) bool { 413 globalSuite.PushItNode(text, func() {}, types.FlagTypePending, codelocation.New(1), 0) 414 return true 415} 416 417//You can mark Specifys as pending using XSpecify 418func XSpecify(text string, is ...interface{}) bool { 419 globalSuite.PushItNode(text, func() {}, types.FlagTypePending, codelocation.New(1), 0) 420 return true 421} 422 423//By allows you to better document large Its. 424// 425//Generally you should try to keep your Its short and to the point. This is not always possible, however, 426//especially in the context of integration tests that capture a particular workflow. 427// 428//By allows you to document such flows. By must be called within a runnable node (It, BeforeEach, Measure, etc...) 429//By will simply log the passed in text to the GinkgoWriter. If By is handed a function it will immediately run the function. 430func By(text string, callbacks ...func()) { 431 preamble := "\x1b[1mSTEP\x1b[0m" 432 if config.DefaultReporterConfig.NoColor { 433 preamble = "STEP" 434 } 435 fmt.Fprintln(GinkgoWriter, preamble+": "+text) 436 if len(callbacks) == 1 { 437 callbacks[0]() 438 } 439 if len(callbacks) > 1 { 440 panic("just one callback per By, please") 441 } 442} 443 444//Measure blocks run the passed in body function repeatedly (determined by the samples argument) 445//and accumulate metrics provided to the Benchmarker by the body function. 446// 447//The body function must have the signature: 448// func(b Benchmarker) 449func Measure(text string, body interface{}, samples int) bool { 450 globalSuite.PushMeasureNode(text, body, types.FlagTypeNone, codelocation.New(1), samples) 451 return true 452} 453 454//You can focus individual Measures using FMeasure 455func FMeasure(text string, body interface{}, samples int) bool { 456 globalSuite.PushMeasureNode(text, body, types.FlagTypeFocused, codelocation.New(1), samples) 457 return true 458} 459 460//You can mark Measurements as pending using PMeasure 461func PMeasure(text string, _ ...interface{}) bool { 462 globalSuite.PushMeasureNode(text, func(b Benchmarker) {}, types.FlagTypePending, codelocation.New(1), 0) 463 return true 464} 465 466//You can mark Measurements as pending using XMeasure 467func XMeasure(text string, _ ...interface{}) bool { 468 globalSuite.PushMeasureNode(text, func(b Benchmarker) {}, types.FlagTypePending, codelocation.New(1), 0) 469 return true 470} 471 472//BeforeSuite blocks are run just once before any specs are run. When running in parallel, each 473//parallel node process will call BeforeSuite. 474// 475//BeforeSuite blocks can be made asynchronous by providing a body function that accepts a Done channel 476// 477//You may only register *one* BeforeSuite handler per test suite. You typically do so in your bootstrap file at the top level. 478func BeforeSuite(body interface{}, timeout ...float64) bool { 479 globalSuite.SetBeforeSuiteNode(body, codelocation.New(1), parseTimeout(timeout...)) 480 return true 481} 482 483//AfterSuite blocks are *always* run after all the specs regardless of whether specs have passed or failed. 484//Moreover, if Ginkgo receives an interrupt signal (^C) it will attempt to run the AfterSuite before exiting. 485// 486//When running in parallel, each parallel node process will call AfterSuite. 487// 488//AfterSuite blocks can be made asynchronous by providing a body function that accepts a Done channel 489// 490//You may only register *one* AfterSuite handler per test suite. You typically do so in your bootstrap file at the top level. 491func AfterSuite(body interface{}, timeout ...float64) bool { 492 globalSuite.SetAfterSuiteNode(body, codelocation.New(1), parseTimeout(timeout...)) 493 return true 494} 495 496//SynchronizedBeforeSuite blocks are primarily meant to solve the problem of setting up singleton external resources shared across 497//nodes when running tests in parallel. For example, say you have a shared database that you can only start one instance of that 498//must be used in your tests. When running in parallel, only one node should set up the database and all other nodes should wait 499//until that node is done before running. 500// 501//SynchronizedBeforeSuite accomplishes this by taking *two* function arguments. The first is only run on parallel node #1. The second is 502//run on all nodes, but *only* after the first function completes succesfully. Ginkgo also makes it possible to send data from the first function (on Node 1) 503//to the second function (on all the other nodes). 504// 505//The functions have the following signatures. The first function (which only runs on node 1) has the signature: 506// 507// func() []byte 508// 509//or, to run asynchronously: 510// 511// func(done Done) []byte 512// 513//The byte array returned by the first function is then passed to the second function, which has the signature: 514// 515// func(data []byte) 516// 517//or, to run asynchronously: 518// 519// func(data []byte, done Done) 520// 521//Here's a simple pseudo-code example that starts a shared database on Node 1 and shares the database's address with the other nodes: 522// 523// var dbClient db.Client 524// var dbRunner db.Runner 525// 526// var _ = SynchronizedBeforeSuite(func() []byte { 527// dbRunner = db.NewRunner() 528// err := dbRunner.Start() 529// Ω(err).ShouldNot(HaveOccurred()) 530// return []byte(dbRunner.URL) 531// }, func(data []byte) { 532// dbClient = db.NewClient() 533// err := dbClient.Connect(string(data)) 534// Ω(err).ShouldNot(HaveOccurred()) 535// }) 536func SynchronizedBeforeSuite(node1Body interface{}, allNodesBody interface{}, timeout ...float64) bool { 537 globalSuite.SetSynchronizedBeforeSuiteNode( 538 node1Body, 539 allNodesBody, 540 codelocation.New(1), 541 parseTimeout(timeout...), 542 ) 543 return true 544} 545 546//SynchronizedAfterSuite blocks complement the SynchronizedBeforeSuite blocks in solving the problem of setting up 547//external singleton resources shared across nodes when running tests in parallel. 548// 549//SynchronizedAfterSuite accomplishes this by taking *two* function arguments. The first runs on all nodes. The second runs only on parallel node #1 550//and *only* after all other nodes have finished and exited. This ensures that node 1, and any resources it is running, remain alive until 551//all other nodes are finished. 552// 553//Both functions have the same signature: either func() or func(done Done) to run asynchronously. 554// 555//Here's a pseudo-code example that complements that given in SynchronizedBeforeSuite. Here, SynchronizedAfterSuite is used to tear down the shared database 556//only after all nodes have finished: 557// 558// var _ = SynchronizedAfterSuite(func() { 559// dbClient.Cleanup() 560// }, func() { 561// dbRunner.Stop() 562// }) 563func SynchronizedAfterSuite(allNodesBody interface{}, node1Body interface{}, timeout ...float64) bool { 564 globalSuite.SetSynchronizedAfterSuiteNode( 565 allNodesBody, 566 node1Body, 567 codelocation.New(1), 568 parseTimeout(timeout...), 569 ) 570 return true 571} 572 573//BeforeEach blocks are run before It blocks. When multiple BeforeEach blocks are defined in nested 574//Describe and Context blocks the outermost BeforeEach blocks are run first. 575// 576//Like It blocks, BeforeEach blocks can be made asynchronous by providing a body function that accepts 577//a Done channel 578func BeforeEach(body interface{}, timeout ...float64) bool { 579 globalSuite.PushBeforeEachNode(body, codelocation.New(1), parseTimeout(timeout...)) 580 return true 581} 582 583//JustBeforeEach blocks are run before It blocks but *after* all BeforeEach blocks. For more details, 584//read the [documentation](http://onsi.github.io/ginkgo/#separating_creation_and_configuration_) 585// 586//Like It blocks, BeforeEach blocks can be made asynchronous by providing a body function that accepts 587//a Done channel 588func JustBeforeEach(body interface{}, timeout ...float64) bool { 589 globalSuite.PushJustBeforeEachNode(body, codelocation.New(1), parseTimeout(timeout...)) 590 return true 591} 592 593//JustAfterEach blocks are run after It blocks but *before* all AfterEach blocks. For more details, 594//read the [documentation](http://onsi.github.io/ginkgo/#separating_creation_and_configuration_) 595// 596//Like It blocks, JustAfterEach blocks can be made asynchronous by providing a body function that accepts 597//a Done channel 598func JustAfterEach(body interface{}, timeout ...float64) bool { 599 globalSuite.PushJustAfterEachNode(body, codelocation.New(1), parseTimeout(timeout...)) 600 return true 601} 602 603//AfterEach blocks are run after It blocks. When multiple AfterEach blocks are defined in nested 604//Describe and Context blocks the innermost AfterEach blocks are run first. 605// 606//Like It blocks, AfterEach blocks can be made asynchronous by providing a body function that accepts 607//a Done channel 608func AfterEach(body interface{}, timeout ...float64) bool { 609 globalSuite.PushAfterEachNode(body, codelocation.New(1), parseTimeout(timeout...)) 610 return true 611} 612 613func parseTimeout(timeout ...float64) time.Duration { 614 if len(timeout) == 0 { 615 return time.Duration(defaultTimeout * int64(time.Second)) 616 } else { 617 return time.Duration(timeout[0] * float64(time.Second)) 618 } 619} 620