Skip to content

Commit e19f556

Browse files
committedJan 29, 2023
Introduce GInkgoHelper() to track and exclude helper functions from porential CodeLcoations
Similar to testing.T.Helper(), GinkgoHelper() makes it easier to write and nest helper functions without having to manage offsets. This commit also updates GinkgoT() to wire up GinkgoT().Helper() to GinkgoHelper()
1 parent 14e7bdd commit e19f556

File tree

13 files changed

+294
-55
lines changed

13 files changed

+294
-55
lines changed
 

‎core_dsl.go

+9
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,15 @@ func GinkgoParallelProcess() int {
163163
return suiteConfig.ParallelProcess
164164
}
165165

166+
/*
167+
GinkgoHelper marks the function it's called in as a test helper. When a failure occurs inside a helper function, Ginkgo will skip the helper when analyzing the stack trace to identify where the failure occurred.
168+
169+
This is an alternative, simpler, mechanism to passing in a skip offset when calling Fail or using Gomega.
170+
*/
171+
func GinkgoHelper() {
172+
types.MarkAsHelper(1)
173+
}
174+
166175
/*
167176
PauseOutputInterception() pauses Ginkgo's output interception. This is only relevant
168177
when running in parallel and output to stdout/stderr is being intercepted. You generally

‎docs/index.md

+79
Original file line numberDiff line numberDiff line change
@@ -1202,6 +1202,66 @@ You must remember to follow this pattern when making assertions in goroutines -
12021202

12031203
When a failure occurs Ginkgo marks the current spec as failed and moves on to the next spec. If, however, you'd like to stop the entire suite when the first failure occurs you can run `ginkgo --fail-fast`.
12041204

1205+
One last thing before we move on. When a failure occurs, Ginkgo records and presents the location of the failure to help you pinpoint where to look to debug your specs. This is typically the line where the call to `Fail` was performed (or, if you're using Gomega, the line where the Gomega assertion failed). Sometimes, however, you need to control the reported location. For example, consider the case where you are using a helper function:
1206+
1207+
```go
1208+
/* === INVALID === */
1209+
func EnsureUserCanRead(book Book, user User) {
1210+
if book.Title == "Les Miserables" && user.Age <= 3 {
1211+
Fail("user is too young for this book") //A
1212+
}
1213+
}
1214+
1215+
It("can read books", func() {
1216+
EnsureUserCanRead(book, user) //B
1217+
user.Read(book)
1218+
})
1219+
```
1220+
1221+
Now, if the `EnsureUserCanRead` helper fails the location presented to the user will point to `//A`. Ideally, however we'd prefer that Ginkgo report `//B`.
1222+
1223+
There are a few ways to solve for this. The first is to pass `Fail` an `offset` like so:
1224+
1225+
```go
1226+
func EnsureUserCanRead(book Book, user User) {
1227+
if book.Title == "Les Miserables" && user.Age <= 3 {
1228+
Fail("user is too young for this book", 1)
1229+
}
1230+
}
1231+
```
1232+
1233+
This will tell Ginkgo to skip a stack frame when calculating the offset. In this particular case Ginkgo will report the location that called `EnsureUserCanRead`: i.e. `//B`.
1234+
1235+
This works... however managing offset can quickly get unwieldy. For example, say we wanted to compose helpers:
1236+
1237+
```go
1238+
func EnsureUserCanCheckout(book Book, user User) {
1239+
EnsureUserCanRead(book, user)
1240+
EnsureUserHasAccessTo(book, user)
1241+
}
1242+
```
1243+
1244+
in _this_ case, we'd need the offset that `EnsureUserCanRead` passes to `Fail` to be `2` instead of `1`.
1245+
1246+
Instead of managing offsets you can use `GinkgoHelper()`:
1247+
1248+
```go
1249+
func EnsureUserCanRead(book Book, user User) {
1250+
GinkgoHelper()
1251+
if book.Title == "Les Miserables" && user.Age <= 3 {
1252+
Fail("user is too young for this book") //note the optional offset is gone
1253+
}
1254+
}
1255+
1256+
func EnsureUserCanCheckout(book Book, user User) {
1257+
GinkgoHelper()
1258+
EnsureUserCanRead(book, user)
1259+
EnsureUserHasAccessTo(book, user)
1260+
}
1261+
```
1262+
1263+
Any function in which `GinkgoHelper()` is called is tracked by Ginkgo and ignored when a failure location is being computed. This allows you to build reusable test helpers and trust that the location presented to the user will always be in the spec that called the helper, and not the helper itself.
1264+
12051265
### Logging Output
12061266
As outlined above, when a spec fails - say via a failed Gomega assertion - Ginkgo will pass the failure message passed to the `Fail` handler. Often times the failure message generated by Gomega gives you enough information to understand and resolve the spec failure.
12071267

@@ -4896,6 +4956,25 @@ now, if the `It` defined in `SharedBehaviorIt` the location reported by Ginkgo w
48964956
48974957
If multiple `Offset`s are provided on a given node, only the last one is used.
48984958
4959+
Lastly, since introducing `Offset` Ginkgo has introduced `GinkgoHelper()` which marks the current function as a test helper who's location should be skipped when determining the location for a node. We generally recommend using `GinkgoHelper()` instead of `Offset()` to manage how locations are computed. The above example could be rewritten as
4960+
4961+
```go
4962+
SharedBehaviorIt := func() {
4963+
GinkgoHelper()
4964+
It("does something common and complicated", func() {
4965+
...
4966+
})
4967+
}
4968+
4969+
Describe("thing A", func() {
4970+
SharedBehaviorIt()
4971+
})
4972+
4973+
Describe("thing B", func() {
4974+
SharedBehaviorIt()
4975+
})
4976+
```
4977+
48994978
#### The CodeLocation Decorator
49004979
In addition to `Offset`, users can decorate nodes with a `types.CodeLocation`. `CodeLocation`s are the structs Ginkgo uses to capture location information. You can, for example, set a custom location using `types.NewCustomCodeLocation(message string)`. Now when the location of the node is emitted the passed in `message` will be printed out instead of the usual `file:line` location.
49014980

‎dsl/core/core_dsl.go

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ var GinkgoLogr = ginkgo.GinkgoLogr
2828
var GinkgoConfiguration = ginkgo.GinkgoConfiguration
2929
var GinkgoRandomSeed = ginkgo.GinkgoRandomSeed
3030
var GinkgoParallelProcess = ginkgo.GinkgoParallelProcess
31+
var GinkgoHelper = ginkgo.GinkgoHelper
3132
var PauseOutputInterception = ginkgo.PauseOutputInterception
3233
var ResumeOutputInterception = ginkgo.ResumeOutputInterception
3334
var RunSpecs = ginkgo.RunSpecs

‎ginkgo_t_dsl.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ GinkgoT() implements an interface analogous to *testing.T and can be used with
77
third-party libraries that accept *testing.T through an interface.
88
99
GinkgoT() takes an optional offset argument that can be used to get the
10-
correct line number associated with the failure.
10+
correct line number associated with the failure - though you do not need to use this if you call GinkgoHelper() or GinkgoT().Helper() appropriately
1111
1212
You can learn more here: https://onsi.github.io/ginkgo/#using-third-party-libraries
1313
*/

‎integration/_fixtures/fail_fixture/fail_fixture_test.go

+9
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,12 @@ var _ = DescribeTable("a top level DescribeTable",
4444
},
4545
Entry("a TableEntry constructed by Entry", 2, 3),
4646
)
47+
48+
var helper = func() {
49+
GinkgoHelper()
50+
Ω("a helper failed").Should(Equal("nope"))
51+
}
52+
53+
var _ = It("tracks line numbers correctly when GinkgoHelper() is called", func() {
54+
helper()
55+
})

‎integration/fail_test.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,10 @@ var _ = Describe("Failing Specs", func() {
3838
Ω(output).Should(MatchRegexp(`a top level DescribeTable \[It\] a TableEntry constructed by Entry\n.*fail_fixture_test\.go:45`),
3939
"the output of a failing Entry should include its file path and line number")
4040

41-
Ω(output).Should(ContainSubstring("0 Passed | 7 Failed"))
41+
Ω(output).Should(ContainSubstring(`a helper failed`))
42+
Ω(output).Should(ContainSubstring(`fail_fixture_test.go:54`), "the code location reported for the helper failure - we're testing the call to GinkgoHelper() works as expected")
43+
44+
Ω(output).Should(ContainSubstring("0 Passed | 8 Failed"))
4245
})
4346
})
4447

‎integration/flags_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,8 @@ var _ = Describe("Flags Specs", func() {
131131
output := string(session.Out.Contents())
132132

133133
Ω(output).Should(ContainSubstring("synchronous failures"))
134-
Ω(output).Should(ContainSubstring("7 Specs"))
135-
Ω(output).Should(ContainSubstring("7 Passed"))
134+
Ω(output).Should(ContainSubstring("8 Specs"))
135+
Ω(output).Should(ContainSubstring("8 Passed"))
136136
Ω(output).Should(ContainSubstring("0 Failed"))
137137
})
138138

‎internal/internal_integration/decorations_test.go

+13
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,17 @@ var _ = Describe("Decorations test", func() {
1414
customIt := func() {
1515
It("is-offset", rt.T("is-offset"), Offset(1))
1616
}
17+
otherCustomIt := func() {
18+
GinkgoHelper()
19+
It("is-also-offset", rt.T("is-also-offset"))
20+
}
1721
var countFlaky = 0
1822
var countRepeat = 0
1923
success, _ := RunFixture("happy-path decoration test", func() {
2024
Describe("top-level-container", func() {
2125
clForOffset = types.NewCodeLocation(0)
2226
customIt()
27+
otherCustomIt()
2328
It("flaky", FlakeAttempts(4), rt.T("flaky", func() {
2429
countFlaky += 1
2530
outputInterceptor.AppendInterceptedOutput("so flaky\n")
@@ -56,6 +61,7 @@ var _ = Describe("Decorations test", func() {
5661
It("runs all the test nodes in the expected order", func() {
5762
Ω(rt).Should(HaveTracked(
5863
"is-offset",
64+
"is-also-offset",
5965
"flaky", "flaky", "flaky",
6066
"flaky-never-passes", "flaky-never-passes",
6167
"flaky-skips",
@@ -72,6 +78,13 @@ var _ = Describe("Decorations test", func() {
7278
})
7379
})
7480

81+
Describe("GinkgoHelper", func() {
82+
It("correctly skips through the stack trace when computing the codelocation", func() {
83+
clForOffset.LineNumber = clForOffset.LineNumber + 2
84+
Ω(reporter.Did.Find("is-also-offset").LeafNodeLocation).Should(Equal(clForOffset))
85+
})
86+
})
87+
7588
Describe("FlakeAttempts", func() {
7689
It("reruns specs until they pass or until the number of flake attempts is exhausted, but does not rerun skipped specs", func() {
7790
Ω(reporter.Did.Find("flaky")).Should(HavePassed(NumAttempts(3), CapturedStdOutput("so flaky\nso flaky\nso flaky\n"), CapturedGinkgoWriterOutput("so tasty\nso tasty\nso tasty\n")))

‎internal/testingtproxy/testing_t_proxy.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ func (t *ginkgoTestingTProxy) Fatalf(format string, args ...interface{}) {
8181
}
8282

8383
func (t *ginkgoTestingTProxy) Helper() {
84-
// No-op
84+
types.MarkAsHelper(1)
8585
}
8686

8787
func (t *ginkgoTestingTProxy) Log(args ...interface{}) {

‎internal/testingtproxy/testingtproxy_test.go

+10-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package testingtproxy_test
22

33
import (
44
"os"
5+
"runtime"
56

67
. "github.com/onsi/ginkgo/v2"
78
. "github.com/onsi/gomega"
@@ -165,7 +166,15 @@ var _ = Describe("Testingtproxy", func() {
165166
})
166167

167168
It("ignores Helper", func() {
168-
GinkgoT().Helper() //is a no-op
169+
cl := func() types.CodeLocation {
170+
GinkgoT().Helper()
171+
return types.NewCodeLocation(0)
172+
}() // this is the expected line
173+
_, fname, lnumber, _ := runtime.Caller(0)
174+
Ω(cl).Should(Equal(types.CodeLocation{
175+
FileName: fname,
176+
LineNumber: lnumber - 1,
177+
}))
169178
})
170179

171180
It("supports Log", func() {

‎table_dsl.go

+20-13
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
/*
1414
The EntryDescription decorator allows you to pass a format string to DescribeTable() and Entry(). This format string is used to generate entry names via:
1515
16-
fmt.Sprintf(formatString, parameters...)
16+
fmt.Sprintf(formatString, parameters...)
1717
1818
where parameters are the parameters passed into the entry.
1919
@@ -32,19 +32,20 @@ DescribeTable describes a table-driven spec.
3232
3333
For example:
3434
35-
DescribeTable("a simple table",
36-
func(x int, y int, expected bool) {
37-
Ω(x > y).Should(Equal(expected))
38-
},
39-
Entry("x > y", 1, 0, true),
40-
Entry("x == y", 0, 0, false),
41-
Entry("x < y", 0, 1, false),
42-
)
35+
DescribeTable("a simple table",
36+
func(x int, y int, expected bool) {
37+
Ω(x > y).Should(Equal(expected))
38+
},
39+
Entry("x > y", 1, 0, true),
40+
Entry("x == y", 0, 0, false),
41+
Entry("x < y", 0, 1, false),
42+
)
4343
4444
You can learn more about DescribeTable here: https://onsi.github.io/ginkgo/#table-specs
4545
And can explore some Table patterns here: https://onsi.github.io/ginkgo/#table-specs-patterns
4646
*/
4747
func DescribeTable(description string, args ...interface{}) bool {
48+
GinkgoHelper()
4849
generateTable(description, args...)
4950
return true
5051
}
@@ -53,6 +54,7 @@ func DescribeTable(description string, args ...interface{}) bool {
5354
You can focus a table with `FDescribeTable`. This is equivalent to `FDescribe`.
5455
*/
5556
func FDescribeTable(description string, args ...interface{}) bool {
57+
GinkgoHelper()
5658
args = append(args, internal.Focus)
5759
generateTable(description, args...)
5860
return true
@@ -62,6 +64,7 @@ func FDescribeTable(description string, args ...interface{}) bool {
6264
You can mark a table as pending with `PDescribeTable`. This is equivalent to `PDescribe`.
6365
*/
6466
func PDescribeTable(description string, args ...interface{}) bool {
67+
GinkgoHelper()
6568
args = append(args, internal.Pending)
6669
generateTable(description, args...)
6770
return true
@@ -95,26 +98,29 @@ If you want to generate interruptible specs simply write a Table function that a
9598
You can learn more about Entry here: https://onsi.github.io/ginkgo/#table-specs
9699
*/
97100
func Entry(description interface{}, args ...interface{}) TableEntry {
101+
GinkgoHelper()
98102
decorations, parameters := internal.PartitionDecorations(args...)
99-
return TableEntry{description: description, decorations: decorations, parameters: parameters, codeLocation: types.NewCodeLocation(1)}
103+
return TableEntry{description: description, decorations: decorations, parameters: parameters, codeLocation: types.NewCodeLocation(0)}
100104
}
101105

102106
/*
103107
You can focus a particular entry with FEntry. This is equivalent to FIt.
104108
*/
105109
func FEntry(description interface{}, args ...interface{}) TableEntry {
110+
GinkgoHelper()
106111
decorations, parameters := internal.PartitionDecorations(args...)
107112
decorations = append(decorations, internal.Focus)
108-
return TableEntry{description: description, decorations: decorations, parameters: parameters, codeLocation: types.NewCodeLocation(1)}
113+
return TableEntry{description: description, decorations: decorations, parameters: parameters, codeLocation: types.NewCodeLocation(0)}
109114
}
110115

111116
/*
112117
You can mark a particular entry as pending with PEntry. This is equivalent to PIt.
113118
*/
114119
func PEntry(description interface{}, args ...interface{}) TableEntry {
120+
GinkgoHelper()
115121
decorations, parameters := internal.PartitionDecorations(args...)
116122
decorations = append(decorations, internal.Pending)
117-
return TableEntry{description: description, decorations: decorations, parameters: parameters, codeLocation: types.NewCodeLocation(1)}
123+
return TableEntry{description: description, decorations: decorations, parameters: parameters, codeLocation: types.NewCodeLocation(0)}
118124
}
119125

120126
/*
@@ -126,7 +132,8 @@ var contextType = reflect.TypeOf(new(context.Context)).Elem()
126132
var specContextType = reflect.TypeOf(new(SpecContext)).Elem()
127133

128134
func generateTable(description string, args ...interface{}) {
129-
cl := types.NewCodeLocation(2)
135+
GinkgoHelper()
136+
cl := types.NewCodeLocation(0)
130137
containerNodeArgs := []interface{}{cl}
131138

132139
entries := []TableEntry{}

‎types/code_location.go

+72-5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"runtime"
88
"runtime/debug"
99
"strings"
10+
"sync"
1011
)
1112

1213
type CodeLocation struct {
@@ -38,21 +39,87 @@ func (codeLocation CodeLocation) ContentsOfLine() string {
3839
return lines[codeLocation.LineNumber-1]
3940
}
4041

42+
type codeLocationLocator struct {
43+
pcs map[uintptr]bool
44+
helpers map[string]bool
45+
lock *sync.Mutex
46+
}
47+
48+
func (c *codeLocationLocator) addHelper(pc uintptr) {
49+
c.lock.Lock()
50+
defer c.lock.Unlock()
51+
52+
if c.pcs[pc] {
53+
return
54+
}
55+
c.lock.Unlock()
56+
f := runtime.FuncForPC(pc)
57+
c.lock.Lock()
58+
if f == nil {
59+
return
60+
}
61+
c.helpers[f.Name()] = true
62+
c.pcs[pc] = true
63+
}
64+
65+
func (c *codeLocationLocator) hasHelper(name string) bool {
66+
c.lock.Lock()
67+
defer c.lock.Unlock()
68+
return c.helpers[name]
69+
}
70+
71+
func (c *codeLocationLocator) getCodeLocation(skip int) CodeLocation {
72+
pc := make([]uintptr, 40)
73+
n := runtime.Callers(skip+2, pc)
74+
if n == 0 {
75+
return CodeLocation{}
76+
}
77+
pc = pc[:n]
78+
frames := runtime.CallersFrames(pc)
79+
for {
80+
frame, more := frames.Next()
81+
if !c.hasHelper(frame.Function) {
82+
return CodeLocation{FileName: frame.File, LineNumber: frame.Line}
83+
}
84+
if !more {
85+
break
86+
}
87+
}
88+
return CodeLocation{}
89+
}
90+
91+
var clLocator = &codeLocationLocator{
92+
pcs: map[uintptr]bool{},
93+
helpers: map[string]bool{},
94+
lock: &sync.Mutex{},
95+
}
96+
97+
// MarkAsHelper is used by GinkgoHelper to mark the caller (appropriately offset by skip)as a helper. You can use this directly if you need to provide an optional `skip` to mark functions further up the call stack as helpers.
98+
func MarkAsHelper(optionalSkip ...int) {
99+
skip := 1
100+
if len(optionalSkip) > 0 {
101+
skip += optionalSkip[0]
102+
}
103+
pc, _, _, ok := runtime.Caller(skip)
104+
if ok {
105+
clLocator.addHelper(pc)
106+
}
107+
}
108+
41109
func NewCustomCodeLocation(message string) CodeLocation {
42110
return CodeLocation{
43111
CustomMessage: message,
44112
}
45113
}
46114

47115
func NewCodeLocation(skip int) CodeLocation {
48-
_, file, line, _ := runtime.Caller(skip + 1)
49-
return CodeLocation{FileName: file, LineNumber: line}
116+
return clLocator.getCodeLocation(skip + 1)
50117
}
51118

52119
func NewCodeLocationWithStackTrace(skip int) CodeLocation {
53-
_, file, line, _ := runtime.Caller(skip + 1)
54-
stackTrace := PruneStack(string(debug.Stack()), skip+1)
55-
return CodeLocation{FileName: file, LineNumber: line, FullStackTrace: stackTrace}
120+
cl := clLocator.getCodeLocation(skip + 1)
121+
cl.FullStackTrace = PruneStack(string(debug.Stack()), skip+1)
122+
return cl
56123
}
57124

58125
// PruneStack removes references to functions that are internal to Ginkgo

‎types/code_location_test.go

+73-31
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package types_test
22

33
import (
4+
"fmt"
45
"runtime"
56

67
. "github.com/onsi/ginkgo/v2"
@@ -9,63 +10,104 @@ import (
910
)
1011

1112
var _ = Describe("CodeLocation", func() {
12-
var codeLocation types.CodeLocation
13-
var expectedFileName string
14-
var expectedLineNumber int
15-
16-
caller0 := func() {
17-
codeLocation = types.NewCodeLocation(1)
13+
clWithSkip := func(skip int) types.CodeLocation {
14+
return types.NewCodeLocation(skip)
1815
}
1916

20-
caller1 := func() {
21-
_, expectedFileName, expectedLineNumber, _ = runtime.Caller(0)
22-
expectedLineNumber += 2
23-
caller0()
17+
helperFunction := func() types.CodeLocation {
18+
GinkgoHelper()
19+
return types.NewCodeLocation(0)
2420
}
2521

26-
BeforeEach(func() {
27-
caller1()
28-
})
22+
Describe("Creating CodeLocations", func() {
23+
Context("when skip is 0", func() {
24+
It("returns the location at which types.NewCodeLocation was called", func() {
25+
_, fname, lnumber, _ := runtime.Caller(0)
26+
cl := types.NewCodeLocation(0)
27+
Ω(cl).Should(Equal(types.CodeLocation{
28+
FileName: fname,
29+
LineNumber: lnumber + 1,
30+
}))
31+
})
32+
})
33+
34+
Context("when skip is > 0", func() {
35+
It("returns the appropriate location from the stack", func() {
36+
_, fname, lnumber, _ := runtime.Caller(0)
37+
cl := clWithSkip(1)
38+
Ω(cl).Should(Equal(types.CodeLocation{
39+
FileName: fname,
40+
LineNumber: lnumber + 1,
41+
}))
42+
43+
_, fname, lnumber, _ = runtime.Caller(0)
44+
cl = func() types.CodeLocation {
45+
return clWithSkip(2)
46+
}() // this is the line that's expected
47+
Ω(cl).Should(Equal(types.CodeLocation{
48+
FileName: fname,
49+
LineNumber: lnumber + 3,
50+
}))
51+
})
52+
})
53+
54+
Describe("when a function has been marked as a helper", func() {
55+
It("does not include that function when generating a code location", func() {
56+
_, fname, lnumber, _ := runtime.Caller(0)
57+
cl := helperFunction()
58+
Ω(cl).Should(Equal(types.CodeLocation{
59+
FileName: fname,
60+
LineNumber: lnumber + 1,
61+
}))
2962

30-
It("should use the passed in skip parameter to pick out the correct file & line number", func() {
31-
Ω(codeLocation.FileName).Should(Equal(expectedFileName))
32-
Ω(codeLocation.LineNumber).Should(Equal(expectedLineNumber))
33-
Ω(codeLocation.FullStackTrace).Should(BeZero())
63+
_, fname, lnumber, _ = runtime.Caller(0)
64+
cl = func() types.CodeLocation {
65+
GinkgoHelper()
66+
return func() types.CodeLocation {
67+
types.MarkAsHelper()
68+
return helperFunction()
69+
}()
70+
}() // this is the line that's expected
71+
Ω(cl).Should(Equal(types.CodeLocation{
72+
FileName: fname,
73+
LineNumber: lnumber + 7,
74+
}))
75+
})
76+
})
3477
})
3578

3679
Describe("stringer behavior", func() {
3780
It("should stringify nicely", func() {
38-
Ω(codeLocation.String()).Should(ContainSubstring("code_location_test.go:%d", expectedLineNumber))
81+
_, fname, lnumber, _ := runtime.Caller(0)
82+
cl := types.NewCodeLocation(0)
83+
Ω(cl.String()).Should(Equal(fmt.Sprintf("%s:%d", fname, lnumber+1)))
3984
})
4085
})
4186

4287
Describe("with a custom message", func() {
43-
BeforeEach(func() {
44-
codeLocation = types.NewCustomCodeLocation("I'm right here.")
45-
})
46-
4788
It("emits the custom message", func() {
48-
Ω(codeLocation.String()).Should(Equal("I'm right here."))
89+
cl := types.NewCustomCodeLocation("I'm right here.")
90+
Ω(cl.String()).Should(Equal("I'm right here."))
4991
})
5092
})
5193

5294
Describe("Fetching the line from the file in question", func() {
5395
It("works", func() {
54-
codeLocation = types.NewCodeLocation(0)
55-
codeLocation.LineNumber = codeLocation.LineNumber - 2
56-
Ω(codeLocation.ContentsOfLine()).Should(Equal("\tDescribe(\"Fetching the line from the file in question\", func() {"))
96+
cl := types.NewCodeLocation(0)
97+
cl.LineNumber = cl.LineNumber - 2
98+
Ω(cl.ContentsOfLine()).Should(Equal("\tDescribe(\"Fetching the line from the file in question\", func() {"))
5799
})
58100

59101
It("returns empty string if the line is not found or is out of bounds", func() {
60-
codeLocation = types.CodeLocation{
102+
cl := types.CodeLocation{
61103
FileName: "foo.go",
62104
LineNumber: 0,
63105
}
64-
Ω(codeLocation.ContentsOfLine()).Should(BeZero())
106+
Ω(cl.ContentsOfLine()).Should(BeZero())
65107

66-
codeLocation = types.NewCodeLocation(0)
67-
codeLocation.LineNumber = codeLocation.LineNumber + 1000000
68-
Ω(codeLocation.ContentsOfLine()).Should(BeZero())
108+
cl = types.NewCodeLocation(0)
109+
cl.LineNumber = cl.LineNumber + 1000000
110+
Ω(cl.ContentsOfLine()).Should(BeZero())
69111
})
70112
})
71113

0 commit comments

Comments
 (0)
Please sign in to comment.