diff --git a/extensions/table/table.go b/extensions/table/table.go index ae8ab7d24..a812c87c5 100644 --- a/extensions/table/table.go +++ b/extensions/table/table.go @@ -13,6 +13,8 @@ import ( "reflect" "github.com/onsi/ginkgo" + "github.com/onsi/ginkgo/internal/codelocation" + "github.com/onsi/ginkgo/types" ) /* @@ -42,7 +44,7 @@ It's important to understand that the `Describe`s and `It`s are generated at eva Individual Entries can be focused (with FEntry) or marked pending (with PEntry or XEntry). In addition, the entire table can be focused or marked pending with FDescribeTable and PDescribeTable/XDescribeTable. */ func DescribeTable(description string, itBody interface{}, entries ...TableEntry) bool { - describeTable(description, itBody, entries, false, false) + describeTable(description, itBody, entries, types.FlagTypeNone) return true } @@ -50,7 +52,7 @@ func DescribeTable(description string, itBody interface{}, entries ...TableEntry You can focus a table with `FDescribeTable`. This is equivalent to `FDescribe`. */ func FDescribeTable(description string, itBody interface{}, entries ...TableEntry) bool { - describeTable(description, itBody, entries, false, true) + describeTable(description, itBody, entries, types.FlagTypeFocused) return true } @@ -58,7 +60,7 @@ func FDescribeTable(description string, itBody interface{}, entries ...TableEntr You can mark a table as pending with `PDescribeTable`. This is equivalent to `PDescribe`. */ func PDescribeTable(description string, itBody interface{}, entries ...TableEntry) bool { - describeTable(description, itBody, entries, true, false) + describeTable(description, itBody, entries, types.FlagTypePending) return true } @@ -66,33 +68,19 @@ func PDescribeTable(description string, itBody interface{}, entries ...TableEntr You can mark a table as pending with `XDescribeTable`. This is equivalent to `XDescribe`. */ func XDescribeTable(description string, itBody interface{}, entries ...TableEntry) bool { - describeTable(description, itBody, entries, true, false) + describeTable(description, itBody, entries, types.FlagTypePending) return true } -func describeTable(description string, itBody interface{}, entries []TableEntry, pending bool, focused bool) { +func describeTable(description string, itBody interface{}, entries []TableEntry, flag types.FlagType) { itBodyValue := reflect.ValueOf(itBody) if itBodyValue.Kind() != reflect.Func { panic(fmt.Sprintf("DescribeTable expects a function, got %#v", itBody)) } - if pending { - ginkgo.PDescribe(description, func() { - for _, entry := range entries { - entry.generateIt(itBodyValue) - } - }) - } else if focused { - ginkgo.FDescribe(description, func() { - for _, entry := range entries { - entry.generateIt(itBodyValue) - } - }) - } else { - ginkgo.Describe(description, func() { - for _, entry := range entries { - entry.generateIt(itBodyValue) - } - }) - } + ginkgo.ExplicitContainerNode(description, func() { + for _, entry := range entries { + entry.generateIt(itBodyValue) + } + }, flag, codelocation.New(2)) } diff --git a/extensions/table/table_entry.go b/extensions/table/table_entry.go index 93f3bc3b0..07403c17f 100644 --- a/extensions/table/table_entry.go +++ b/extensions/table/table_entry.go @@ -4,6 +4,8 @@ import ( "reflect" "github.com/onsi/ginkgo" + "github.com/onsi/ginkgo/internal/codelocation" + "github.com/onsi/ginkgo/types" ) /* @@ -12,13 +14,15 @@ TableEntry represents an entry in a table test. You generally use the `Entry` c type TableEntry struct { Description string Parameters []interface{} - Pending bool - Focused bool + Flag types.FlagType + + // TODO: what if this isn't set? + codeLocation types.CodeLocation } func (t TableEntry) generateIt(itBody reflect.Value) { - if t.Pending { - ginkgo.PIt(t.Description) + if t.Flag == types.FlagTypePending { + ginkgo.ExplicitItNode(t.Description, func() {}, types.FlagTypePending, t.codeLocation, 0) return } @@ -33,15 +37,9 @@ func (t TableEntry) generateIt(itBody reflect.Value) { } } - body := func() { + ginkgo.ExplicitItNode(t.Description, func() { itBody.Call(values) - } - - if t.Focused { - ginkgo.FIt(t.Description, body) - } else { - ginkgo.It(t.Description, body) - } + }, t.Flag, t.codeLocation, 0) } /* @@ -53,26 +51,26 @@ Subsequent parameters are saved off and sent to the callback passed in to `Descr Each Entry ends up generating an individual Ginkgo It. */ func Entry(description string, parameters ...interface{}) TableEntry { - return TableEntry{description, parameters, false, false} + return TableEntry{description, parameters, types.FlagTypeNone, codelocation.New(1)} } /* You can focus a particular entry with FEntry. This is equivalent to FIt. */ func FEntry(description string, parameters ...interface{}) TableEntry { - return TableEntry{description, parameters, false, true} + return TableEntry{description, parameters, types.FlagTypeFocused, codelocation.New(1)} } /* You can mark a particular entry as pending with PEntry. This is equivalent to PIt. */ func PEntry(description string, parameters ...interface{}) TableEntry { - return TableEntry{description, parameters, true, false} + return TableEntry{description, parameters, types.FlagTypePending, codelocation.New(1)} } /* You can mark a particular entry as pending with XEntry. This is equivalent to XIt. */ func XEntry(description string, parameters ...interface{}) TableEntry { - return TableEntry{description, parameters, true, false} + return TableEntry{description, parameters, types.FlagTypePending, codelocation.New(1)} } diff --git a/ginkgo_dsl.go b/ginkgo_dsl.go index 3cbf89a35..743ecdc14 100644 --- a/ginkgo_dsl.go +++ b/ginkgo_dsl.go @@ -615,6 +615,22 @@ func AfterEach(body interface{}, timeout ...float64) bool { return true } +//ExplicitItNode adds a fully-specified It node to the global context. This function is largely only +//useful for meta-programming or adding on to the Ginkgo DSL, when writing normal test cases it's best +//to use one of the others. +func ExplicitItNode(text string, body interface{}, flag types.FlagType, codeLocation types.CodeLocation, timeout time.Duration) bool { + globalSuite.PushItNode(text, body, flag, codeLocation, timeout) + return true +} + +//ExplicitContainerNode adds a fully-specified Container node to the global context. This function is +//largely only useful for meta-programming or adding on to the Ginkgo DSL, when writing normal test +//cases it's best to use one of the others. +func ExplicitContainerNode(text string, body func(), flag types.FlagType, codeLocation types.CodeLocation) bool { + globalSuite.PushContainerNode(text, body, flag, codeLocation) + return true +} + func parseTimeout(timeout ...float64) time.Duration { if len(timeout) == 0 { return time.Duration(defaultTimeout * int64(time.Second)) diff --git a/integration/_fixtures/failing_table_tests/failing_table_tests_suite_test.go b/integration/_fixtures/failing_table_tests/failing_table_tests_suite_test.go new file mode 100644 index 000000000..4c0ad54bc --- /dev/null +++ b/integration/_fixtures/failing_table_tests/failing_table_tests_suite_test.go @@ -0,0 +1,13 @@ +package failing_table_tests + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "testing" +) + +func TestFocused_fixture(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Failing_table_tests Suite") +} diff --git a/integration/_fixtures/failing_table_tests/failing_table_tests_test.go b/integration/_fixtures/failing_table_tests/failing_table_tests_test.go new file mode 100644 index 000000000..903a888bb --- /dev/null +++ b/integration/_fixtures/failing_table_tests/failing_table_tests_test.go @@ -0,0 +1,16 @@ +package failing_table_tests + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/extensions/table" + . "github.com/onsi/gomega" +) + +var _ = Describe("FailingTableTests", func() { + + DescribeTable("a failing entry", + func(val bool) { Ω(val).Should(BeTrue()) }, + Entry("passing", true), + Entry("failing", false), + ) +}) diff --git a/integration/run_test.go b/integration/run_test.go index 6c270b61b..10a7d458a 100644 --- a/integration/run_test.go +++ b/integration/run_test.go @@ -16,7 +16,8 @@ import ( ) var _ = Describe("Running Specs", func() { - var pathToTest string + // Provide a default, else a BeforeEach with an accidental redeclaration (`:=`) will cause the integration test suite to recurse infinitely. + pathToTest := "invalid_path" isWindows := (runtime.GOOS == "windows") denoter := "•" @@ -353,6 +354,41 @@ var _ = Describe("Running Specs", func() { }) }) + Context("when pointed at a packages with a failing table test", func() { + BeforeEach(func() { + pathToTest = tmpPath("failing_table_tests") + copyIn(fixturePath("failing_table_tests"), pathToTest, false) + }) + + It("should identify the failing entry", func() { + session := startGinkgo(pathToTest, "--noColor") + Eventually(session).Should(gexec.Exit(1)) + output := string(session.Out.Contents()) + + Ω(output).Should(ContainSubstring("Test Suite Failed")) + Ω(output).Should(ContainSubstring(fmt.Sprintf("%s Failure", denoter))) + + Ω(output).Should(ContainSubstring("Summarizing 1 Failure:")) + Ω(output).Should(ContainSubstring("[Fail] FailingTableTests a failing entry [It] failing")) + + // The table + Ω(output).Should( + ContainSubstring("failing_table_tests/failing_table_tests_test.go:11"), + "point to the failing table") + Ω(output).ShouldNot(MatchRegexp(`github.com/onsi/ginkgo/extensions/table/table.go:\[\d+\]`)) + + // The entry + Ω(output).Should( + ContainSubstring("failing_table_tests/failing_table_tests_test.go:14"), + "point to the failing entry") + Ω(output).ShouldNot(MatchRegexp(`github.com/onsi/ginkgo/extensions/table/table_entry.go:\[\d+\]`)) + + Ω(output).Should( + ContainSubstring("failing_table_tests/failing_table_tests_test.go:12"), + "point to the failing assertion") + }) + }) + Context("when running recursively", func() { BeforeEach(func() { passingTest := tmpPath("A")