github.com/onsi/ginkgo@v1.16.6-0.20211118180735-4e1925ba4c95/internal/node_test.go (about)

     1  package internal_test
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"reflect"
     7  	"time"
     8  
     9  	. "github.com/onsi/ginkgo"
    10  	"github.com/onsi/ginkgo/types"
    11  	. "github.com/onsi/gomega"
    12  	"github.com/onsi/gomega/gmeasure"
    13  
    14  	"github.com/onsi/ginkgo/internal"
    15  )
    16  
    17  var _ = Describe("UniqueNodeID", func() {
    18  	It("returns a unique id every time it's called", func() {
    19  		Ω(internal.UniqueNodeID()).ShouldNot(Equal(internal.UniqueNodeID()))
    20  	})
    21  })
    22  
    23  var _ = Describe("Partitioning Decorations", func() {
    24  	It("separates out decorations and non-decorations", func() {
    25  		type Foo struct {
    26  			A int
    27  		}
    28  		decorations, remaining := internal.PartitionDecorations(
    29  			Offset(3),
    30  			Foo{3},
    31  			types.NewCustomCodeLocation("hey there"),
    32  			"hey there",
    33  			Focus,
    34  			2.0,
    35  			Pending,
    36  			Serial,
    37  			Ordered,
    38  			nil,
    39  			1,
    40  			[]interface{}{Focus, Pending, []interface{}{Offset(2), Serial, FlakeAttempts(2)}, Ordered, Label("a", "b", "c")},
    41  			[]interface{}{1, 2, 3.1, nil},
    42  			[]string{"a", "b", "c"},
    43  			Label("A", "B", "C"),
    44  			Label("D"),
    45  			[]interface{}{},
    46  			FlakeAttempts(1),
    47  			true,
    48  		)
    49  
    50  		Ω(decorations).Should(Equal([]interface{}{
    51  			Offset(3),
    52  			types.NewCustomCodeLocation("hey there"),
    53  			Focus,
    54  			Pending,
    55  			Serial,
    56  			Ordered,
    57  			[]interface{}{Focus, Pending, []interface{}{Offset(2), Serial, FlakeAttempts(2)}, Ordered, Label("a", "b", "c")},
    58  			Label("A", "B", "C"),
    59  			Label("D"),
    60  			FlakeAttempts(1),
    61  		}))
    62  
    63  		Ω(remaining).Should(Equal([]interface{}{
    64  			Foo{3},
    65  			"hey there",
    66  			2.0,
    67  			nil,
    68  			1,
    69  			[]interface{}{1, 2, 3.1, nil},
    70  			[]string{"a", "b", "c"},
    71  			[]interface{}{},
    72  			true,
    73  		}))
    74  	})
    75  })
    76  
    77  var _ = Describe("Combining Labels", func() {
    78  	It("can combine labels and produce the unique union", func() {
    79  		Ω(internal.UnionOfLabels(Label("a", "b", "c"), Label("b", "c", "d"), Label("e", "a", "f"))).Should(Equal(Label("a", "b", "c", "d", "e", "f")))
    80  	})
    81  })
    82  
    83  var _ = Describe("Constructing nodes", func() {
    84  	var dt *types.DeprecationTracker
    85  	var didRun bool
    86  	var body func()
    87  	BeforeEach(func() {
    88  		dt = types.NewDeprecationTracker()
    89  		didRun = false
    90  		body = func() { didRun = true }
    91  	})
    92  
    93  	ExpectAllWell := func(errors []error) {
    94  		ExpectWithOffset(1, errors).Should(BeEmpty())
    95  		ExpectWithOffset(1, dt.DidTrackDeprecations()).Should(BeFalse())
    96  	}
    97  
    98  	Describe("happy path", func() {
    99  		It("creates a node with a non-zero id", func() {
   100  			node, errors := internal.NewNode(dt, ntIt, "text", body, cl, Focus, Label("A", "B", "C"))
   101  			Ω(node.ID).Should(BeNumerically(">", 0))
   102  			Ω(node.NodeType).Should(Equal(ntIt))
   103  			Ω(node.Text).Should(Equal("text"))
   104  			node.Body()
   105  			Ω(didRun).Should(BeTrue())
   106  			Ω(node.CodeLocation).Should(Equal(cl))
   107  			Ω(node.MarkedFocus).Should(BeTrue())
   108  			Ω(node.MarkedPending).Should(BeFalse())
   109  			Ω(node.NestingLevel).Should(Equal(-1))
   110  			Ω(node.Labels).Should(Equal(Labels{"A", "B", "C"}))
   111  			ExpectAllWell(errors)
   112  		})
   113  	})
   114  
   115  	Describe("Assigning CodeLocation", func() {
   116  		Context("with nothing explicitly specified ", func() {
   117  			It("assumes a base-offset of 2", func() {
   118  				cl := types.NewCodeLocation(1)
   119  				node, errors := internal.NewNode(dt, ntIt, "text", body)
   120  				Ω(node.CodeLocation.FileName).Should(Equal(cl.FileName))
   121  				ExpectAllWell(errors)
   122  			})
   123  		})
   124  
   125  		Context("specifying code locations", func() {
   126  			It("uses the last passed-in code location", func() {
   127  				cl2 := types.NewCustomCodeLocation("hi")
   128  				node, errors := internal.NewNode(dt, ntIt, "text", body, cl, cl2)
   129  				Ω(node.CodeLocation).Should(Equal(cl2))
   130  				ExpectAllWell(errors)
   131  			})
   132  		})
   133  
   134  		Context("specifying offets", func() {
   135  			It("takes the offset and adds it to the base-offset of 2 to compute the code location", func() {
   136  				cl := types.NewCodeLocation(2)
   137  				cl2 := types.NewCustomCodeLocation("hi")
   138  				node, errors := internal.NewNode(dt, ntIt, "text", body, cl2, Offset(1))
   139  				//note that Offset overrides cl2
   140  				Ω(node.CodeLocation.FileName).Should(Equal(cl.FileName))
   141  				ExpectAllWell(errors)
   142  			})
   143  		})
   144  	})
   145  
   146  	Describe("ignoring deprecated timeouts", func() {
   147  		It("ignores any float64s", func() {
   148  			node, errors := internal.NewNode(dt, ntIt, "text", body, 3.141, 2.71)
   149  			node.Body()
   150  			Ω(didRun).Should(BeTrue())
   151  			ExpectAllWell(errors)
   152  		})
   153  	})
   154  
   155  	Describe("the Focus and Pending decorations", func() {
   156  		It("the node is neither Focused nor Pending by default", func() {
   157  			node, errors := internal.NewNode(dt, ntIt, "text", body)
   158  			Ω(node.MarkedFocus).Should(BeFalse())
   159  			Ω(node.MarkedPending).Should(BeFalse())
   160  			ExpectAllWell(errors)
   161  		})
   162  		It("marks the node as focused", func() {
   163  			node, errors := internal.NewNode(dt, ntIt, "text", body, Focus)
   164  			Ω(node.MarkedFocus).Should(BeTrue())
   165  			Ω(node.MarkedPending).Should(BeFalse())
   166  			ExpectAllWell(errors)
   167  		})
   168  		It("marks the node as pending", func() {
   169  			node, errors := internal.NewNode(dt, ntIt, "text", body, Pending)
   170  			Ω(node.MarkedFocus).Should(BeFalse())
   171  			Ω(node.MarkedPending).Should(BeTrue())
   172  			ExpectAllWell(errors)
   173  		})
   174  		It("errors when both Focus and Pending are set", func() {
   175  			node, errors := internal.NewNode(dt, ntIt, "text", body, cl, Focus, Pending)
   176  			Ω(node).Should(BeZero())
   177  			Ω(errors).Should(ConsistOf(types.GinkgoErrors.InvalidDeclarationOfFocusedAndPending(cl, ntIt)))
   178  		})
   179  		It("allows containers to be marked", func() {
   180  			node, errors := internal.NewNode(dt, ntCon, "text", body, Focus)
   181  			Ω(node.MarkedFocus).Should(BeTrue())
   182  			Ω(node.MarkedPending).Should(BeFalse())
   183  			ExpectAllWell(errors)
   184  
   185  			node, errors = internal.NewNode(dt, ntCon, "text", body, Pending)
   186  			Ω(node.MarkedFocus).Should(BeFalse())
   187  			Ω(node.MarkedPending).Should(BeTrue())
   188  			ExpectAllWell(errors)
   189  		})
   190  		It("does not allow non-container/it nodes to be marked", func() {
   191  			node, errors := internal.NewNode(dt, ntBef, "", body, cl, Focus)
   192  			Ω(node).Should(BeZero())
   193  			Ω(errors).Should(ConsistOf(types.GinkgoErrors.InvalidDecoratorForNodeType(cl, ntBef, "Focus")))
   194  
   195  			node, errors = internal.NewNode(dt, ntAf, "", body, cl, Pending)
   196  			Ω(node).Should(BeZero())
   197  			Ω(errors).Should(ConsistOf(types.GinkgoErrors.InvalidDecoratorForNodeType(cl, ntAf, "Pending")))
   198  
   199  			Ω(dt.DidTrackDeprecations()).Should(BeFalse())
   200  		})
   201  	})
   202  
   203  	Describe("the Serial decoration", func() {
   204  		It("the node is not Serial by default", func() {
   205  			node, errors := internal.NewNode(dt, ntIt, "text", body)
   206  			Ω(node.MarkedSerial).Should(BeFalse())
   207  			ExpectAllWell(errors)
   208  		})
   209  		It("marks the node as Serial", func() {
   210  			node, errors := internal.NewNode(dt, ntIt, "text", body, Serial)
   211  			Ω(node.MarkedSerial).Should(BeTrue())
   212  			ExpectAllWell(errors)
   213  		})
   214  		It("allows containers to be marked", func() {
   215  			node, errors := internal.NewNode(dt, ntCon, "text", body, Serial)
   216  			Ω(node.MarkedSerial).Should(BeTrue())
   217  			ExpectAllWell(errors)
   218  		})
   219  		It("does not allow non-container/it nodes to be marked", func() {
   220  			node, errors := internal.NewNode(dt, ntBef, "", body, cl, Serial)
   221  			Ω(node).Should(BeZero())
   222  			Ω(errors).Should(ConsistOf(types.GinkgoErrors.InvalidDecoratorForNodeType(cl, ntBef, "Serial")))
   223  			Ω(dt.DidTrackDeprecations()).Should(BeFalse())
   224  		})
   225  	})
   226  
   227  	Describe("the Ordered decoration", func() {
   228  		It("the node is not Ordered by default", func() {
   229  			node, errors := internal.NewNode(dt, ntCon, "", body)
   230  			Ω(node.MarkedOrdered).Should(BeFalse())
   231  			ExpectAllWell(errors)
   232  		})
   233  		It("marks the node as Ordered", func() {
   234  			node, errors := internal.NewNode(dt, ntCon, "", body, Ordered)
   235  			Ω(node.MarkedOrdered).Should(BeTrue())
   236  			ExpectAllWell(errors)
   237  		})
   238  		It("does not allow non-container nodes to be marked", func() {
   239  			node, errors := internal.NewNode(dt, ntBef, "", body, cl, Ordered)
   240  			Ω(node).Should(BeZero())
   241  			Ω(errors).Should(ConsistOf(types.GinkgoErrors.InvalidDecoratorForNodeType(cl, ntBef, "Ordered")))
   242  			Ω(dt.DidTrackDeprecations()).Should(BeFalse())
   243  
   244  			node, errors = internal.NewNode(dt, ntIt, "not even Its", body, cl, Ordered)
   245  			Ω(node).Should(BeZero())
   246  			Ω(errors).Should(ConsistOf(types.GinkgoErrors.InvalidDecoratorForNodeType(cl, ntIt, "Ordered")))
   247  			Ω(dt.DidTrackDeprecations()).Should(BeFalse())
   248  		})
   249  	})
   250  
   251  	Describe("The FlakeAttempts decoration", func() {
   252  		It("is zero by default", func() {
   253  			node, errors := internal.NewNode(dt, ntIt, "text", body)
   254  			Ω(node).ShouldNot(BeZero())
   255  			Ω(node.FlakeAttempts).Should(Equal(0))
   256  			ExpectAllWell(errors)
   257  		})
   258  		It("sets the FlakeAttempts field", func() {
   259  			node, errors := internal.NewNode(dt, ntIt, "text", body, FlakeAttempts(2))
   260  			Ω(node.FlakeAttempts).Should(Equal(2))
   261  			ExpectAllWell(errors)
   262  		})
   263  		It("can be applied to containers", func() {
   264  			node, errors := internal.NewNode(dt, ntCon, "text", body, FlakeAttempts(2))
   265  			Ω(node.FlakeAttempts).Should(Equal(2))
   266  			ExpectAllWell(errors)
   267  		})
   268  		It("cannot be applied to non-container/it nodes", func() {
   269  			node, errors := internal.NewNode(dt, ntBef, "", body, cl, FlakeAttempts(2))
   270  			Ω(node).Should(BeZero())
   271  			Ω(errors).Should(ConsistOf(types.GinkgoErrors.InvalidDecoratorForNodeType(cl, ntBef, "FlakeAttempts")))
   272  			Ω(dt.DidTrackDeprecations()).Should(BeFalse())
   273  		})
   274  	})
   275  
   276  	Describe("The Label decoration", func() {
   277  		It("has no labels by default", func() {
   278  			node, errors := internal.NewNode(dt, ntIt, "text", body)
   279  			Ω(node).ShouldNot(BeZero())
   280  			Ω(node.Labels).Should(Equal(Labels{}))
   281  			ExpectAllWell(errors)
   282  		})
   283  
   284  		It("can track labels", func() {
   285  			node, errors := internal.NewNode(dt, ntIt, "text", body, Label("A", "B", "C"))
   286  			Ω(node.Labels).Should(Equal(Labels{"A", "B", "C"}))
   287  			ExpectAllWell(errors)
   288  		})
   289  
   290  		It("appends and dedupes all labels together, even if nested", func() {
   291  			node, errors := internal.NewNode(dt, ntIt, "text", body, Label("A", "B", "C"), Label("D", "E", "C"), []interface{}{Label("F"), []interface{}{Label("G", "H", "A", "F")}})
   292  			Ω(node.Labels).Should(Equal(Labels{"A", "B", "C", "D", "E", "F", "G", "H"}))
   293  			ExpectAllWell(errors)
   294  		})
   295  
   296  		It("can be applied to containers", func() {
   297  			node, errors := internal.NewNode(dt, ntCon, "text", body, Label("A", "B", "C"))
   298  			Ω(node.Labels).Should(Equal(Labels{"A", "B", "C"}))
   299  			ExpectAllWell(errors)
   300  		})
   301  
   302  		It("cannot be applied to non-container/it nodes", func() {
   303  			node, errors := internal.NewNode(dt, ntBef, "", body, cl, Label("A", "B", "C"))
   304  			Ω(node).Should(BeZero())
   305  			Ω(errors).Should(ConsistOf(types.GinkgoErrors.InvalidDecoratorForNodeType(cl, ntBef, "Label")))
   306  			Ω(dt.DidTrackDeprecations()).Should(BeFalse())
   307  		})
   308  
   309  		It("validates labels", func() {
   310  			node, errors := internal.NewNode(dt, ntIt, "", body, cl, Label("A", "B&C", "C,D", "C,D ", "  "))
   311  			Ω(node).Should(BeZero())
   312  			Ω(errors).Should(ConsistOf(types.GinkgoErrors.InvalidLabel("B&C", cl), types.GinkgoErrors.InvalidLabel("C,D", cl), types.GinkgoErrors.InvalidLabel("C,D ", cl), types.GinkgoErrors.InvalidEmptyLabel(cl)))
   313  			Ω(dt.DidTrackDeprecations()).Should(BeFalse())
   314  		})
   315  	})
   316  
   317  	Describe("passing in functions", func() {
   318  		It("works when a single function is passed in", func() {
   319  			node, errors := internal.NewNode(dt, ntIt, "text", body, cl)
   320  			node.Body()
   321  			Ω(didRun).Should(BeTrue())
   322  			ExpectAllWell(errors)
   323  		})
   324  
   325  		It("allows deprecated async functions and registers a deprecation warning", func() {
   326  			node, errors := internal.NewNode(dt, ntIt, "text", func(done Done) {
   327  				didRun = true
   328  				Ω(done).ShouldNot(BeNil())
   329  				close(done)
   330  			}, cl)
   331  			node.Body()
   332  			Ω(didRun).Should(BeTrue())
   333  			Ω(errors).Should(BeEmpty())
   334  			Ω(dt.DeprecationsReport()).Should(ContainSubstring(types.Deprecations.Async().Message))
   335  		})
   336  
   337  		It("errors if more than one function is provided", func() {
   338  			node, errors := internal.NewNode(dt, ntIt, "text", body, body, cl)
   339  			Ω(node).Should(BeZero())
   340  			Ω(errors).Should(ConsistOf(types.GinkgoErrors.MultipleBodyFunctions(cl, ntIt)))
   341  			Ω(dt.DidTrackDeprecations()).Should(BeFalse())
   342  		})
   343  
   344  		It("errors if the function has a return value", func() {
   345  			f := func() string { return "" }
   346  			node, errors := internal.NewNode(dt, ntIt, "text", f, cl)
   347  			Ω(node).Should(BeZero())
   348  			Ω(errors).Should(ConsistOf(types.GinkgoErrors.InvalidBodyType(reflect.TypeOf(f), cl, ntIt)))
   349  			Ω(dt.DidTrackDeprecations()).Should(BeFalse())
   350  		})
   351  
   352  		It("errors if the function takes more than one argument", func() {
   353  			f := func(Done, string) {}
   354  			node, errors := internal.NewNode(dt, ntIt, "text", f, cl)
   355  			Ω(node).Should(BeZero())
   356  			Ω(errors).Should(ConsistOf(types.GinkgoErrors.InvalidBodyType(reflect.TypeOf(f), cl, ntIt)))
   357  			Ω(dt.DidTrackDeprecations()).Should(BeFalse())
   358  		})
   359  
   360  		It("errors if the function takes one argument and that argument is not the deprecated Done channel", func() {
   361  			f := func(chan interface{}) {}
   362  			node, errors := internal.NewNode(dt, ntIt, "text", f, cl)
   363  			Ω(node).Should(BeZero())
   364  			Ω(errors).Should(ConsistOf(types.GinkgoErrors.InvalidBodyType(reflect.TypeOf(f), cl, ntIt)))
   365  			Ω(dt.DidTrackDeprecations()).Should(BeFalse())
   366  		})
   367  
   368  		It("errors if no function is passed in", func() {
   369  			node, errors := internal.NewNode(dt, ntIt, "text", cl)
   370  			Ω(node).Should(BeZero())
   371  			Ω(errors).Should(ConsistOf(types.GinkgoErrors.MissingBodyFunction(cl, ntIt)))
   372  			Ω(dt.DidTrackDeprecations()).Should(BeFalse())
   373  		})
   374  
   375  		It("is ok if no function is passed in but it is marked pending", func() {
   376  			node, errors := internal.NewNode(dt, ntIt, "text", cl, Pending)
   377  			Ω(node.IsZero()).Should(BeFalse())
   378  			ExpectAllWell(errors)
   379  		})
   380  	})
   381  
   382  	Describe("non-recognized decorations", func() {
   383  		It("errors when a non-recognized decoration is provided", func() {
   384  			node, errors := internal.NewNode(dt, ntIt, "text", cl, body, Focus, "aardvark", 5)
   385  			Ω(node).Should(BeZero())
   386  			Ω(errors).Should(ConsistOf(
   387  				types.GinkgoErrors.UnknownDecorator(cl, ntIt, "aardvark"),
   388  				types.GinkgoErrors.UnknownDecorator(cl, ntIt, 5),
   389  			))
   390  			Ω(dt.DidTrackDeprecations()).Should(BeFalse())
   391  		})
   392  	})
   393  
   394  	Describe("when decorations are nested in slices", func() {
   395  		It("unrolls them first", func() {
   396  			node, errors := internal.NewNode(dt, ntIt, "text", []interface{}{body, []interface{}{Focus, FlakeAttempts(3), Label("A")}, FlakeAttempts(2), Label("B"), Label("C", "D")})
   397  			Ω(node.FlakeAttempts).Should(Equal(2))
   398  			Ω(node.MarkedFocus).Should(BeTrue())
   399  			Ω(node.Labels).Should(Equal(Labels{"A", "B", "C", "D"}))
   400  			node.Body()
   401  			Ω(didRun).Should(BeTrue())
   402  			ExpectAllWell(errors)
   403  		})
   404  	})
   405  })
   406  
   407  var _ = Describe("Node", func() {
   408  	Describe("The other node constructors", func() {
   409  		Describe("NewSynchronizedBeforeSuiteNode", func() {
   410  			It("returns a correctly configured node", func() {
   411  				var ranProc1, ranAllProcs bool
   412  				proc1Body := func() []byte { ranProc1 = true; return nil }
   413  				allProcsBody := func(_ []byte) { ranAllProcs = true }
   414  				node, errors := internal.NewSynchronizedBeforeSuiteNode(proc1Body, allProcsBody, cl)
   415  				Ω(errors).Should(BeEmpty())
   416  				Ω(node.ID).Should(BeNumerically(">", 0))
   417  				Ω(node.NodeType).Should(Equal(types.NodeTypeSynchronizedBeforeSuite))
   418  
   419  				node.SynchronizedBeforeSuiteProc1Body()
   420  				Ω(ranProc1).Should(BeTrue())
   421  
   422  				node.SynchronizedBeforeSuiteAllProcsBody(nil)
   423  				Ω(ranAllProcs).Should(BeTrue())
   424  
   425  				Ω(node.CodeLocation).Should(Equal(cl))
   426  				Ω(node.NestingLevel).Should(Equal(0))
   427  			})
   428  		})
   429  
   430  		Describe("NewSynchronizedAfterSuiteNode", func() {
   431  			It("returns a correctly configured node", func() {
   432  				var ranProc1, ranAllProcs bool
   433  				allProcsBody := func() { ranAllProcs = true }
   434  				proc1Body := func() { ranProc1 = true }
   435  
   436  				node, errors := internal.NewSynchronizedAfterSuiteNode(allProcsBody, proc1Body, cl)
   437  				Ω(errors).Should(BeEmpty())
   438  				Ω(node.ID).Should(BeNumerically(">", 0))
   439  				Ω(node.NodeType).Should(Equal(types.NodeTypeSynchronizedAfterSuite))
   440  
   441  				node.SynchronizedAfterSuiteAllProcsBody()
   442  				Ω(ranAllProcs).Should(BeTrue())
   443  
   444  				node.SynchronizedAfterSuiteProc1Body()
   445  				Ω(ranProc1).Should(BeTrue())
   446  
   447  				Ω(node.CodeLocation).Should(Equal(cl))
   448  				Ω(node.NestingLevel).Should(Equal(0))
   449  			})
   450  		})
   451  
   452  		Describe("NewReportBeforeEachNode", func() {
   453  			It("returns a correctly configured node", func() {
   454  				var didRun bool
   455  				body := func(types.SpecReport) { didRun = true }
   456  
   457  				node, errors := internal.NewReportBeforeEachNode(body, cl)
   458  				Ω(errors).Should(BeEmpty())
   459  				Ω(node.ID).Should(BeNumerically(">", 0))
   460  				Ω(node.NodeType).Should(Equal(types.NodeTypeReportBeforeEach))
   461  
   462  				node.ReportEachBody(types.SpecReport{})
   463  				Ω(didRun).Should(BeTrue())
   464  
   465  				Ω(node.CodeLocation).Should(Equal(cl))
   466  				Ω(node.NestingLevel).Should(Equal(-1))
   467  			})
   468  		})
   469  
   470  		Describe("NewReportAfterEachNode", func() {
   471  			It("returns a correctly configured node", func() {
   472  				var didRun bool
   473  				body := func(types.SpecReport) { didRun = true }
   474  
   475  				node, errors := internal.NewReportAfterEachNode(body, cl)
   476  				Ω(errors).Should(BeEmpty())
   477  				Ω(node.ID).Should(BeNumerically(">", 0))
   478  				Ω(node.NodeType).Should(Equal(types.NodeTypeReportAfterEach))
   479  
   480  				node.ReportEachBody(types.SpecReport{})
   481  				Ω(didRun).Should(BeTrue())
   482  
   483  				Ω(node.CodeLocation).Should(Equal(cl))
   484  				Ω(node.NestingLevel).Should(Equal(-1))
   485  			})
   486  		})
   487  
   488  		Describe("NewReportAfterSuiteNode", func() {
   489  			It("returns a correctly configured node", func() {
   490  				var didRun bool
   491  				body := func(types.Report) { didRun = true }
   492  				node, errors := internal.NewReportAfterSuiteNode("my custom report", body, cl)
   493  				Ω(errors).Should(BeEmpty())
   494  				Ω(node.Text).Should(Equal("my custom report"))
   495  				Ω(node.ID).Should(BeNumerically(">", 0))
   496  				Ω(node.NodeType).Should(Equal(types.NodeTypeReportAfterSuite))
   497  
   498  				node.ReportAfterSuiteBody(types.Report{})
   499  				Ω(didRun).Should(BeTrue())
   500  
   501  				Ω(node.CodeLocation).Should(Equal(cl))
   502  				Ω(node.NestingLevel).Should(Equal(0))
   503  			})
   504  		})
   505  
   506  		Describe("NewCleanupNode", func() {
   507  			var capturedFailure string
   508  			var capturedCL types.CodeLocation
   509  
   510  			var failFunc = func(msg string, cl types.CodeLocation) {
   511  				capturedFailure = msg
   512  				capturedCL = cl
   513  			}
   514  
   515  			BeforeEach(func() {
   516  				capturedFailure = ""
   517  				capturedCL = types.CodeLocation{}
   518  			})
   519  
   520  			Context("when passed no function", func() {
   521  				It("errors", func() {
   522  					node, errs := internal.NewCleanupNode(failFunc, cl)
   523  					Ω(node.IsZero()).Should(BeTrue())
   524  					Ω(errs).Should(ConsistOf(types.GinkgoErrors.DeferCleanupInvalidFunction(cl)))
   525  					Ω(capturedFailure).Should(BeZero())
   526  					Ω(capturedCL).Should(BeZero())
   527  				})
   528  			})
   529  
   530  			Context("when passed a function that returns too many values", func() {
   531  				It("errors", func() {
   532  					node, errs := internal.NewCleanupNode(failFunc, cl, func() (int, error) {
   533  						return 0, nil
   534  					})
   535  					Ω(node.IsZero()).Should(BeTrue())
   536  					Ω(errs).Should(ConsistOf(types.GinkgoErrors.DeferCleanupInvalidFunction(cl)))
   537  					Ω(capturedFailure).Should(BeZero())
   538  					Ω(capturedCL).Should(BeZero())
   539  				})
   540  			})
   541  
   542  			Context("when passed a function that does not return", func() {
   543  				It("creates a body that runs the function and never calls the fail handler", func() {
   544  					didRun := false
   545  					node, errs := internal.NewCleanupNode(failFunc, cl, func() {
   546  						didRun = true
   547  					})
   548  					Ω(node.CodeLocation).Should(Equal(cl))
   549  					Ω(node.NodeType).Should(Equal(types.NodeTypeCleanupInvalid))
   550  					Ω(errs).Should(BeEmpty())
   551  
   552  					node.Body()
   553  					Ω(didRun).Should(BeTrue())
   554  					Ω(capturedFailure).Should(BeZero())
   555  					Ω(capturedCL).Should(BeZero())
   556  				})
   557  			})
   558  
   559  			Context("when passed a function that returns nil", func() {
   560  				It("creates a body that runs the function and does not call the fail handler", func() {
   561  					didRun := false
   562  					node, errs := internal.NewCleanupNode(failFunc, cl, func() error {
   563  						didRun = true
   564  						return nil
   565  					})
   566  					Ω(node.CodeLocation).Should(Equal(cl))
   567  					Ω(node.NodeType).Should(Equal(types.NodeTypeCleanupInvalid))
   568  					Ω(errs).Should(BeEmpty())
   569  
   570  					node.Body()
   571  					Ω(didRun).Should(BeTrue())
   572  					Ω(capturedFailure).Should(BeZero())
   573  					Ω(capturedCL).Should(BeZero())
   574  				})
   575  			})
   576  
   577  			Context("when passed a function that returns an error", func() {
   578  				It("creates a body that runs the function and does not call the fail handler", func() {
   579  					didRun := false
   580  					node, errs := internal.NewCleanupNode(failFunc, cl, func() error {
   581  						didRun = true
   582  						return fmt.Errorf("welp")
   583  					})
   584  					Ω(node.CodeLocation).Should(Equal(cl))
   585  					Ω(node.NodeType).Should(Equal(types.NodeTypeCleanupInvalid))
   586  					Ω(errs).Should(BeEmpty())
   587  
   588  					node.Body()
   589  					Ω(didRun).Should(BeTrue())
   590  					Ω(capturedFailure).Should(Equal("DeferCleanup callback returned error: welp"))
   591  					Ω(capturedCL).Should(Equal(cl))
   592  				})
   593  			})
   594  
   595  			Context("when passed a function that takes arguments, and those arguments", func() {
   596  				It("creates a body that runs the function and passes in those arguments", func() {
   597  					var inA, inB, inC = "A", 2, "C"
   598  					var receivedA, receivedC string
   599  					var receivedB int
   600  					node, errs := internal.NewCleanupNode(failFunc, cl, func(a string, b int, c string) error {
   601  						receivedA, receivedB, receivedC = a, b, c
   602  						return nil
   603  					}, inA, inB, inC)
   604  					inA, inB, inC = "floop", 3, "flarp"
   605  					Ω(node.CodeLocation).Should(Equal(cl))
   606  					Ω(node.NodeType).Should(Equal(types.NodeTypeCleanupInvalid))
   607  					Ω(errs).Should(BeEmpty())
   608  
   609  					node.Body()
   610  					Ω(receivedA).Should(Equal("A"))
   611  					Ω(receivedB).Should(Equal(2))
   612  					Ω(receivedC).Should(Equal("C"))
   613  					Ω(capturedFailure).Should(BeZero())
   614  					Ω(capturedCL).Should(BeZero())
   615  				})
   616  			})
   617  
   618  			Context("controlling the cleanup's code location", func() {
   619  				It("computes its own when one is not provided", func() {
   620  					node, errs := func() (internal.Node, []error) {
   621  						return internal.NewCleanupNode(failFunc, func() error {
   622  							return fmt.Errorf("welp")
   623  						})
   624  					}()
   625  					localCL := types.NewCodeLocation(0)
   626  					localCL.LineNumber -= 1
   627  					Ω(node.CodeLocation).Should(Equal(localCL))
   628  					Ω(node.NodeType).Should(Equal(types.NodeTypeCleanupInvalid))
   629  					Ω(errs).Should(BeEmpty())
   630  
   631  					node.Body()
   632  					Ω(capturedFailure).Should(Equal("DeferCleanup callback returned error: welp"))
   633  					Ω(capturedCL).Should(Equal(localCL))
   634  				})
   635  
   636  				It("can accept an Offset", func() {
   637  					node, errs := func() (internal.Node, []error) {
   638  						return func() (internal.Node, []error) {
   639  							return internal.NewCleanupNode(failFunc, Offset(1), func() error {
   640  								return fmt.Errorf("welp")
   641  							})
   642  						}()
   643  					}()
   644  					localCL := types.NewCodeLocation(0)
   645  					localCL.LineNumber -= 1
   646  					Ω(node.CodeLocation).Should(Equal(localCL))
   647  					Ω(node.NodeType).Should(Equal(types.NodeTypeCleanupInvalid))
   648  					Ω(errs).Should(BeEmpty())
   649  
   650  					node.Body()
   651  					Ω(capturedFailure).Should(Equal("DeferCleanup callback returned error: welp"))
   652  					Ω(capturedCL).Should(Equal(localCL))
   653  
   654  				})
   655  
   656  				It("can accept a code location", func() {
   657  					node, errs := internal.NewCleanupNode(failFunc, cl, func() error {
   658  						return fmt.Errorf("welp")
   659  					})
   660  					Ω(node.CodeLocation).Should(Equal(cl))
   661  					Ω(node.NodeType).Should(Equal(types.NodeTypeCleanupInvalid))
   662  					Ω(errs).Should(BeEmpty())
   663  
   664  					node.Body()
   665  					Ω(capturedFailure).Should(Equal("DeferCleanup callback returned error: welp"))
   666  					Ω(capturedCL).Should(Equal(cl))
   667  				})
   668  			})
   669  		})
   670  	})
   671  
   672  	Describe("IsZero()", func() {
   673  		It("returns true if the node is zero", func() {
   674  			Ω(Node{}.IsZero()).Should(BeTrue())
   675  		})
   676  
   677  		It("returns false if the node is non-zero", func() {
   678  			node, errors := internal.NewNode(nil, ntIt, "hummus", func() {}, cl)
   679  			Ω(errors).Should(BeEmpty())
   680  			Ω(node.IsZero()).Should(BeFalse())
   681  		})
   682  	})
   683  })
   684  
   685  var _ = Describe("Nodes", func() {
   686  	Describe("CopyAppend", func() {
   687  		var n1, n2, n3, n4 Node
   688  
   689  		BeforeEach(func() {
   690  			n1, n2, n3, n4 = N(), N(), N(), N()
   691  		})
   692  
   693  		It("appends the passed in nodes and returns the result", func() {
   694  			result := Nodes{n1, n2}.CopyAppend(n3, n4)
   695  			Ω(result).Should(Equal(Nodes{n1, n2, n3, n4}))
   696  		})
   697  
   698  		It("makes a copy, leaving the original untouched", func() {
   699  			original := Nodes{n1, n2}
   700  			original.CopyAppend(n3, n4)
   701  			Ω(original).Should(Equal(Nodes{n1, n2}))
   702  		})
   703  	})
   704  
   705  	Describe("SplitAround", func() {
   706  		var nodes Nodes
   707  
   708  		BeforeEach(func() {
   709  			nodes = Nodes{N(), N(), N(), N(), N()}
   710  		})
   711  
   712  		Context("when the pivot is a member of nodes", func() {
   713  			Context("when the pivot is not at one of the ends", func() {
   714  				It("returns the correct left and right nodes", func() {
   715  					left, right := nodes.SplitAround(nodes[2])
   716  					Ω(left).Should(Equal(Nodes{nodes[0], nodes[1]}))
   717  					Ω(right).Should(Equal(Nodes{nodes[3], nodes[4]}))
   718  				})
   719  			})
   720  
   721  			Context("when the pivot is the first member", func() {
   722  				It("returns an empty left nodes and the complete right nodes", func() {
   723  					left, right := nodes.SplitAround(nodes[0])
   724  					Ω(left).Should(BeEmpty())
   725  					Ω(right).Should(Equal(Nodes{nodes[1], nodes[2], nodes[3], nodes[4]}))
   726  
   727  				})
   728  			})
   729  
   730  			Context("when the pivot is the last member", func() {
   731  				It("returns an empty right nodes and the complete left nodes", func() {
   732  					left, right := nodes.SplitAround(nodes[4])
   733  					Ω(left).Should(Equal(Nodes{nodes[0], nodes[1], nodes[2], nodes[3]}))
   734  					Ω(right).Should(BeEmpty())
   735  				})
   736  			})
   737  		})
   738  
   739  		Context("when the pivot is not in nodes", func() {
   740  			It("returns an empty right nodes and the complete left nodes", func() {
   741  				left, right := nodes.SplitAround(N())
   742  				Ω(left).Should(Equal(nodes))
   743  				Ω(right).Should(BeEmpty())
   744  			})
   745  		})
   746  	})
   747  
   748  	Describe("FirstNodeWithType", func() {
   749  		var nodes Nodes
   750  
   751  		BeforeEach(func() {
   752  			nodes = Nodes{N(ntCon), N("bef1", ntBef), N("bef2", ntBef), N(ntIt), N(ntAf)}
   753  		})
   754  
   755  		Context("when there is a matching node", func() {
   756  			It("returns the first node that matches one of the requested node types", func() {
   757  				Ω(nodes.FirstNodeWithType(ntAf | ntIt | ntBef).Text).Should(Equal("bef1"))
   758  			})
   759  		})
   760  		Context("when there is no matching node", func() {
   761  			It("returns an empty node", func() {
   762  				Ω(nodes.FirstNodeWithType(ntJusAf)).Should(BeZero())
   763  			})
   764  		})
   765  	})
   766  
   767  	Describe("Filtering By NodeType", func() {
   768  		var nCon, nBef1, nBef2, nIt, nAf Node
   769  		var nodes Nodes
   770  
   771  		BeforeEach(func() {
   772  			nCon = N(ntCon)
   773  			nBef1 = N(ntBef)
   774  			nBef2 = N(ntBef)
   775  			nIt = N(ntIt)
   776  			nAf = N(ntAf)
   777  			nodes = Nodes{nCon, nBef1, nBef2, nIt, nAf}
   778  		})
   779  
   780  		Describe("WithType", func() {
   781  			Context("when there are matching nodes", func() {
   782  				It("returns them while preserving order", func() {
   783  					Ω(nodes.WithType(ntIt | ntBef)).Should(Equal(Nodes{nBef1, nBef2, nIt}))
   784  				})
   785  			})
   786  
   787  			Context("when there are no matching nodes", func() {
   788  				It("returns an empty Nodes{}", func() {
   789  					Ω(nodes.WithType(ntJusAf)).Should(BeEmpty())
   790  				})
   791  			})
   792  		})
   793  
   794  		Describe("WithoutType", func() {
   795  			Context("when there are matching nodes", func() {
   796  				It("does not include them in the result", func() {
   797  					Ω(nodes.WithoutType(ntIt | ntBef)).Should(Equal(Nodes{nCon, nAf}))
   798  				})
   799  			})
   800  
   801  			Context("when no nodes match", func() {
   802  				It("doesn't elide any nodes", func() {
   803  					Ω(nodes.WithoutType(ntJusAf)).Should(Equal(nodes))
   804  				})
   805  			})
   806  		})
   807  
   808  		Describe("WithoutNode", func() {
   809  			Context("when operating on an empty nodes list", func() {
   810  				It("does nothing", func() {
   811  					nodes = Nodes{}
   812  					Ω(nodes.WithoutNode(N(ntIt))).Should(BeEmpty())
   813  
   814  				})
   815  			})
   816  			Context("when the node is in the nodes list", func() {
   817  				It("returns a copy of the nodes list without the node in it", func() {
   818  					Ω(nodes.WithoutNode(nBef2)).Should(Equal(Nodes{nCon, nBef1, nIt, nAf}))
   819  					Ω(nodes).Should(Equal(Nodes{nCon, nBef1, nBef2, nIt, nAf}))
   820  				})
   821  			})
   822  
   823  			Context("when the node is not in the nodes list", func() {
   824  				It("returns an unadulterated copy of the nodes list", func() {
   825  					Ω(nodes.WithoutNode(N(ntBef))).Should(Equal(Nodes{nCon, nBef1, nBef2, nIt, nAf}))
   826  					Ω(nodes).Should(Equal(Nodes{nCon, nBef1, nBef2, nIt, nAf}))
   827  				})
   828  			})
   829  		})
   830  
   831  		Describe("Filter", func() {
   832  			It("returns a copy of the nodes list containing nodes that pass the filter", func() {
   833  				filtered := nodes.Filter(func(n Node) bool {
   834  					return n.NodeType.Is(types.NodeTypeBeforeEach | types.NodeTypeIt)
   835  				})
   836  				Ω(filtered).Should(Equal(Nodes{nBef1, nBef2, nIt}))
   837  				Ω(nodes).Should(Equal(Nodes{nCon, nBef1, nBef2, nIt, nAf}))
   838  
   839  				filtered = nodes.Filter(func(n Node) bool {
   840  					return false
   841  				})
   842  				Ω(filtered).Should(BeEmpty())
   843  			})
   844  		})
   845  	})
   846  
   847  	Describe("SortedByDescendingNestingLevel", func() {
   848  		var n0A, n0B, n1A, n1B, n1C, n2A, n2B Node
   849  		var nodes Nodes
   850  		BeforeEach(func() {
   851  			n0A = N(NestingLevel(0))
   852  			n0B = N(NestingLevel(0))
   853  			n1A = N(NestingLevel(1))
   854  			n1B = N(NestingLevel(1))
   855  			n1C = N(NestingLevel(1))
   856  			n2A = N(NestingLevel(2))
   857  			n2B = N(NestingLevel(2))
   858  			nodes = Nodes{n0A, n0B, n1A, n1B, n1C, n2A, n2B}
   859  		})
   860  
   861  		It("returns copy sorted by descending nesting level, preserving order within nesting level", func() {
   862  			Ω(nodes.SortedByDescendingNestingLevel()).Should(Equal(Nodes{n2A, n2B, n1A, n1B, n1C, n0A, n0B}))
   863  			Ω(nodes).Should(Equal(Nodes{n0A, n0B, n1A, n1B, n1C, n2A, n2B}), "original nodes should not have been modified")
   864  		})
   865  	})
   866  
   867  	Describe("SortedByAscendingNestingLevel", func() {
   868  		var n0A, n0B, n1A, n1B, n1C, n2A, n2B Node
   869  		var nodes Nodes
   870  		BeforeEach(func() {
   871  			n0A = N(NestingLevel(0))
   872  			n0B = N(NestingLevel(0))
   873  			n1A = N(NestingLevel(1))
   874  			n1B = N(NestingLevel(1))
   875  			n1C = N(NestingLevel(1))
   876  			n2A = N(NestingLevel(2))
   877  			n2B = N(NestingLevel(2))
   878  			nodes = Nodes{n2A, n1A, n1B, n0A, n2B, n0B, n1C}
   879  		})
   880  
   881  		It("returns copy sorted by ascending nesting level, preserving order within nesting level", func() {
   882  			Ω(nodes.SortedByAscendingNestingLevel()).Should(Equal(Nodes{n0A, n0B, n1A, n1B, n1C, n2A, n2B}))
   883  			Ω(nodes).Should(Equal(Nodes{n2A, n1A, n1B, n0A, n2B, n0B, n1C}), "original nodes should not have been modified")
   884  		})
   885  	})
   886  
   887  	Describe("WithinNestingLevel", func() {
   888  		var n0, n1, n2a, n2b, n3 Node
   889  		var nodes Nodes
   890  		BeforeEach(func() {
   891  			n0 = N(NestingLevel(0))
   892  			n1 = N(NestingLevel(1))
   893  			n2a = N(NestingLevel(2))
   894  			n3 = N(NestingLevel(3))
   895  			n2b = N(NestingLevel(2))
   896  			nodes = Nodes{n0, n1, n2a, n3, n2b}
   897  		})
   898  
   899  		It("returns nodes, in order, with nesting level equal to or less than the requested level", func() {
   900  			Ω(nodes.WithinNestingLevel(-1)).Should(BeEmpty())
   901  			Ω(nodes.WithinNestingLevel(0)).Should(Equal(Nodes{n0}))
   902  			Ω(nodes.WithinNestingLevel(1)).Should(Equal(Nodes{n0, n1}))
   903  			Ω(nodes.WithinNestingLevel(2)).Should(Equal(Nodes{n0, n1, n2a, n2b}))
   904  			Ω(nodes.WithinNestingLevel(3)).Should(Equal(Nodes{n0, n1, n2a, n3, n2b}))
   905  		})
   906  	})
   907  
   908  	Describe("Reverse", func() {
   909  		It("reverses the nodes", func() {
   910  			nodes := Nodes{N("A"), N("B"), N("C"), N("D"), N("E")}
   911  			Ω(nodes.Reverse().Texts()).Should(Equal([]string{"E", "D", "C", "B", "A"}))
   912  		})
   913  
   914  		It("works with empty nodes", func() {
   915  			nodes := Nodes{}
   916  			Ω(nodes.Reverse()).Should(Equal(Nodes{}))
   917  		})
   918  	})
   919  
   920  	Describe("Texts", func() {
   921  		var nodes Nodes
   922  		BeforeEach(func() {
   923  			nodes = Nodes{N("the first node"), N(""), N("2"), N("c"), N("")}
   924  		})
   925  
   926  		It("returns a string slice containing the individual node text strings in order", func() {
   927  			Ω(nodes.Texts()).Should(Equal([]string{"the first node", "", "2", "c", ""}))
   928  		})
   929  	})
   930  
   931  	Describe("Labels and UnionOfLabels", func() {
   932  		var nodes Nodes
   933  		BeforeEach(func() {
   934  			nodes = Nodes{N(Label("A", "B")), N(Label("C")), N(), N(Label("A")), N(Label("D")), N(Label("B", "D", "E"))}
   935  		})
   936  
   937  		It("Labels returns a slice containing the labels for each node in order", func() {
   938  			Ω(nodes.Labels()).Should(Equal([][]string{
   939  				{"A", "B"},
   940  				{"C"},
   941  				{},
   942  				{"A"},
   943  				{"D"},
   944  				{"B", "D", "E"},
   945  			}))
   946  		})
   947  
   948  		It("UnionOfLabels returns a single slice of labels harvested from all nodes and deduped", func() {
   949  			Ω(nodes.UnionOfLabels()).Should(Equal([]string{"A", "B", "C", "D", "E"}))
   950  		})
   951  	})
   952  
   953  	Describe("CodeLocation", func() {
   954  		var nodes Nodes
   955  		var cl1, cl2 types.CodeLocation
   956  		BeforeEach(func() {
   957  			cl1 = types.NewCodeLocation(0)
   958  			cl2 = types.NewCodeLocation(0)
   959  			nodes = Nodes{N(cl1), N(cl2), N()}
   960  		})
   961  
   962  		It("returns a types.CodeLocation sice containing the individual node code locations in order", func() {
   963  			Ω(nodes.CodeLocations()).Should(Equal([]types.CodeLocation{cl1, cl2, cl}))
   964  		})
   965  	})
   966  
   967  	Describe("BestTextFor", func() {
   968  		var nIt, nBef1, nBef2 Node
   969  		var nodes Nodes
   970  		BeforeEach(func() {
   971  			nIt = N("an it", ntIt, NestingLevel(2))
   972  			nBef1 = N(ntBef, NestingLevel(2))
   973  			nBef2 = N(ntBef, NestingLevel(4))
   974  			nodes = Nodes{
   975  				N("the root container", ntCon, NestingLevel(0)),
   976  				N("the inner container", ntCon, NestingLevel(1)),
   977  				nBef1,
   978  				nIt,
   979  				nBef2,
   980  			}
   981  		})
   982  
   983  		Context("when the passed in node has text", func() {
   984  			It("returns that text", func() {
   985  				Ω(nodes.BestTextFor(nIt)).Should(Equal("an it"))
   986  			})
   987  		})
   988  
   989  		Context("when the node has no text", func() {
   990  			Context("and there is a node one-nesting-level-up with text", func() {
   991  				It("returns that node's text", func() {
   992  					Ω(nodes.BestTextFor(nBef1)).Should(Equal("the inner container"))
   993  				})
   994  			})
   995  
   996  			Context("and there is no node one-nesting-level up with text", func() {
   997  				It("returns empty string", func() {
   998  					Ω(nodes.BestTextFor(nBef2)).Should(Equal(""))
   999  				})
  1000  			})
  1001  		})
  1002  	})
  1003  
  1004  	Describe("ContainsNodeID", func() {
  1005  		Context("when there is a node with the matching ID", func() {
  1006  			It("returns true", func() {
  1007  				nodes := Nodes{N(), N(), N()}
  1008  				Ω(nodes.ContainsNodeID(nodes[1].ID)).Should(BeTrue())
  1009  			})
  1010  		})
  1011  
  1012  		Context("when there is no node with matching ID", func() {
  1013  			It("returns false", func() {
  1014  				nodes := Nodes{N(), N(), N()}
  1015  				Ω(nodes.ContainsNodeID(nodes[2].ID + 1)).Should(BeFalse())
  1016  			})
  1017  		})
  1018  	})
  1019  
  1020  	Describe("HasNodeMarkedPending", func() {
  1021  		Context("when there is a node marked pending", func() {
  1022  			It("returns true", func() {
  1023  				nodes := Nodes{N(), N(), N(Pending), N()}
  1024  				Ω(nodes.HasNodeMarkedPending()).Should(BeTrue())
  1025  			})
  1026  		})
  1027  
  1028  		Context("when there is no node marked pending", func() {
  1029  			It("returns false", func() {
  1030  				nodes := Nodes{N(), N(), N()}
  1031  				Ω(nodes.HasNodeMarkedPending()).Should(BeFalse())
  1032  			})
  1033  		})
  1034  	})
  1035  
  1036  	Describe("HasNodeMarkedFocus", func() {
  1037  		Context("when there is a node marked focus", func() {
  1038  			It("returns true", func() {
  1039  				nodes := Nodes{N(), N(), N(Focus), N()}
  1040  				Ω(nodes.HasNodeMarkedFocus()).Should(BeTrue())
  1041  			})
  1042  		})
  1043  
  1044  		Context("when there is no node marked focus", func() {
  1045  			It("returns false", func() {
  1046  				nodes := Nodes{N(), N(), N()}
  1047  				Ω(nodes.HasNodeMarkedFocus()).Should(BeFalse())
  1048  			})
  1049  		})
  1050  	})
  1051  
  1052  	Describe("HasNodeMarkedSerial", func() {
  1053  		Context("when there is a node marked serial", func() {
  1054  			It("returns true", func() {
  1055  				nodes := Nodes{N(), N(), N(Serial), N()}
  1056  				Ω(nodes.HasNodeMarkedSerial()).Should(BeTrue())
  1057  			})
  1058  		})
  1059  
  1060  		Context("when there is no node marked serial", func() {
  1061  			It("returns false", func() {
  1062  				nodes := Nodes{N(), N(), N()}
  1063  				Ω(nodes.HasNodeMarkedSerial()).Should(BeFalse())
  1064  			})
  1065  		})
  1066  	})
  1067  
  1068  	Describe("FirstNodeMarkedOrdered", func() {
  1069  		Context("when there are nodes marked ordered", func() {
  1070  			It("returns the first one", func() {
  1071  				nodes := Nodes{N(), N("A", ntCon, Ordered), N("B", ntCon, Ordered), N()}
  1072  				Ω(nodes.FirstNodeMarkedOrdered().Text).Should(Equal("A"))
  1073  			})
  1074  		})
  1075  
  1076  		Context("when there is no node marked ordered", func() {
  1077  			It("returns zero", func() {
  1078  				nodes := Nodes{N(), N(), N()}
  1079  				Ω(nodes.FirstNodeMarkedOrdered()).Should(BeZero())
  1080  			})
  1081  		})
  1082  	})
  1083  })
  1084  
  1085  var _ = Describe("Iteration Performance", Serial, Label("performance"), func() {
  1086  	BeforeEach(func() {
  1087  		if os.Getenv("PERF") == "" {
  1088  			Skip("")
  1089  		}
  1090  	})
  1091  
  1092  	It("compares the performance of iteration using range vs counters", func() {
  1093  		experiment := gmeasure.NewExperiment("iteration")
  1094  
  1095  		size := 1000
  1096  		nodes := make(Nodes, size)
  1097  		for i := 0; i < size; i++ {
  1098  			nodes[i] = N(ntAf)
  1099  		}
  1100  		nodes[size-1] = N(ntIt)
  1101  
  1102  		experiment.SampleDuration("range", func(idx int) {
  1103  			numIts := 0
  1104  			for _, node := range nodes {
  1105  				if node.NodeType.Is(ntIt) {
  1106  					numIts += 1
  1107  				}
  1108  			}
  1109  		}, gmeasure.SamplingConfig{N: 1024}, gmeasure.Precision(time.Nanosecond))
  1110  
  1111  		experiment.SampleDuration("range-index", func(idx int) {
  1112  			numIts := 0
  1113  			for i := range nodes {
  1114  				if nodes[i].NodeType.Is(ntIt) {
  1115  					numIts += 1
  1116  				}
  1117  			}
  1118  		}, gmeasure.SamplingConfig{N: 1024}, gmeasure.Precision(time.Nanosecond))
  1119  
  1120  		experiment.SampleDuration("counter", func(idx int) {
  1121  			numIts := 0
  1122  			for i := 0; i < len(nodes); i++ {
  1123  				if nodes[i].NodeType.Is(ntIt) {
  1124  					numIts += 1
  1125  				}
  1126  			}
  1127  		}, gmeasure.SamplingConfig{N: 1024}, gmeasure.Precision(time.Nanosecond))
  1128  
  1129  		AddReportEntry(experiment.Name, gmeasure.RankStats(gmeasure.LowerMedianIsBetter, experiment.GetStats("range"), experiment.GetStats("range-index"), experiment.GetStats("counter")))
  1130  
  1131  	})
  1132  
  1133  	It("compares the performance of slice construction by growing slices vs pre-allocating slices vs counting twice", func() {
  1134  		experiment := gmeasure.NewExperiment("filtering")
  1135  
  1136  		size := 1000
  1137  		nodes := make(Nodes, size)
  1138  		for i := 0; i < size; i++ {
  1139  			if i%100 == 0 {
  1140  				nodes[i] = N(ntIt)
  1141  			} else {
  1142  				nodes[i] = N(ntAf)
  1143  			}
  1144  		}
  1145  
  1146  		largeStats := []gmeasure.Stats{}
  1147  		smallStats := []gmeasure.Stats{}
  1148  
  1149  		experiment.SampleDuration("grow-slice (large)", func(idx int) {
  1150  			out := Nodes{}
  1151  			for i := range nodes {
  1152  				if nodes[i].NodeType.Is(ntAf) {
  1153  					out = append(out, nodes[i])
  1154  				}
  1155  			}
  1156  		}, gmeasure.SamplingConfig{N: 1024}, gmeasure.Precision(time.Nanosecond))
  1157  		largeStats = append(largeStats, experiment.GetStats("grow-slice (large)"))
  1158  
  1159  		experiment.SampleDuration("grow-slice (small)", func(idx int) {
  1160  			out := Nodes{}
  1161  			for i := range nodes {
  1162  				if nodes[i].NodeType.Is(ntIt) {
  1163  					out = append(out, nodes[i])
  1164  				}
  1165  			}
  1166  		}, gmeasure.SamplingConfig{N: 1024}, gmeasure.Precision(time.Nanosecond))
  1167  		smallStats = append(smallStats, experiment.GetStats("grow-slice (small)"))
  1168  
  1169  		experiment.SampleDuration("pre-allocate (large)", func(idx int) {
  1170  			out := make(Nodes, 0, len(nodes))
  1171  			for i := range nodes {
  1172  				if nodes[i].NodeType.Is(ntAf) {
  1173  					out = append(out, nodes[i])
  1174  				}
  1175  			}
  1176  		}, gmeasure.SamplingConfig{N: 1024}, gmeasure.Precision(time.Nanosecond))
  1177  		largeStats = append(largeStats, experiment.GetStats("pre-allocate (large)"))
  1178  
  1179  		experiment.SampleDuration("pre-allocate (small)", func(idx int) {
  1180  			out := make(Nodes, 0, len(nodes))
  1181  			for i := range nodes {
  1182  				if nodes[i].NodeType.Is(ntIt) {
  1183  					out = append(out, nodes[i])
  1184  				}
  1185  			}
  1186  		}, gmeasure.SamplingConfig{N: 1024}, gmeasure.Precision(time.Nanosecond))
  1187  		smallStats = append(smallStats, experiment.GetStats("pre-allocate (small)"))
  1188  
  1189  		experiment.SampleDuration("pre-count (large)", func(idx int) {
  1190  			count := 0
  1191  			for i := range nodes {
  1192  				if nodes[i].NodeType.Is(ntAf) {
  1193  					count++
  1194  				}
  1195  			}
  1196  
  1197  			out := make(Nodes, count)
  1198  			j := 0
  1199  			for i := range nodes {
  1200  				if nodes[i].NodeType.Is(ntAf) {
  1201  					out[j] = nodes[i]
  1202  					j++
  1203  				}
  1204  			}
  1205  		}, gmeasure.SamplingConfig{N: 1024}, gmeasure.Precision(time.Nanosecond))
  1206  		largeStats = append(largeStats, experiment.GetStats("pre-count (large)"))
  1207  
  1208  		experiment.SampleDuration("pre-count (small)", func(idx int) {
  1209  			count := 0
  1210  			for i := range nodes {
  1211  				if nodes[i].NodeType.Is(ntIt) {
  1212  					count++
  1213  				}
  1214  			}
  1215  
  1216  			out := make(Nodes, count)
  1217  			j := 0
  1218  			for i := range nodes {
  1219  				if nodes[i].NodeType.Is(ntIt) {
  1220  					out[j] = nodes[i]
  1221  					j++
  1222  				}
  1223  			}
  1224  		}, gmeasure.SamplingConfig{N: 1024}, gmeasure.Precision(time.Nanosecond))
  1225  		smallStats = append(smallStats, experiment.GetStats("pre-count (small)"))
  1226  
  1227  		AddReportEntry("Large Slice", gmeasure.RankStats(gmeasure.LowerMedianIsBetter, largeStats...))
  1228  		AddReportEntry("Small Slice", gmeasure.RankStats(gmeasure.LowerMedianIsBetter, smallStats...))
  1229  	})
  1230  })