1
1
/*
2
-
3
2
package gleak complements the Gingko/Gomega testing and matchers framework with
4
3
matchers for Goroutine leakage detection.
5
4
6
- Basics of nleak
5
+ # Basics of gleak
7
6
8
7
To start with,
9
8
10
- Goroutines()
9
+ Goroutines()
11
10
12
11
returns information about all (non-dead) goroutines at a particular moment. This
13
12
is useful to capture a known correct snapshot and then later taking a new
14
13
snapshot and comparing these two snapshots for leaked goroutines.
15
14
16
15
Next, the matcher
17
16
18
- HaveLeaked(...)
17
+ HaveLeaked(...)
19
18
20
19
filters out well-known and expected "non-leaky" goroutines from an actual list
21
20
of goroutines (passed from Eventually or Expect), hopefully ending up with an
@@ -26,67 +25,67 @@ because no one wants leaked goroutines.
26
25
27
26
A typical pattern to detect goroutines leaked in individual tests is as follows:
28
27
29
- var ignoreGood []Goroutine
28
+ var ignoreGood []Goroutine
30
29
31
- BeforeEach(func() {
32
- ignoreGood = Goroutines()
33
- })
30
+ BeforeEach(func() {
31
+ ignoreGood = Goroutines()
32
+ })
34
33
35
- AfterEach(func() {
36
- // Note: it's "Goroutines", but not "Goroutines()", when using with Eventually!
37
- Eventually(Goroutines).ShouldNot(HaveLeaked(ignoreGood))
38
- })
34
+ AfterEach(func() {
35
+ // Note: it's "Goroutines", but not "Goroutines()", when using with Eventually!
36
+ Eventually(Goroutines).ShouldNot(HaveLeaked(ignoreGood))
37
+ })
39
38
40
39
Using Eventually instead of Expect ensures that there is some time given for
41
40
temporary goroutines to finally wind down. Gomega's default values apply: the 1s
42
41
timeout and 10ms polling interval.
43
42
44
43
Please note that the form
45
44
46
- HaveLeaked(ignoreGood)
45
+ HaveLeaked(ignoreGood)
47
46
48
47
is the same as the slightly longer, but also more expressive variant:
49
48
50
- HaveLeaked(IgnoringGoroutines(ignoreGood))
49
+ HaveLeaked(IgnoringGoroutines(ignoreGood))
51
50
52
- Leak-Related Matchers
51
+ # Leak-Related Matchers
53
52
54
53
Depending on your tests and the dependencies used, you might need to identify
55
54
additional goroutines as not being leaks. The gleak packages comes with the
56
55
following predefined goroutine "filter" matchers that can be specified as
57
56
arguments to HaveLeaked(...):
58
57
59
- IgnoringTopFunction("foo.bar") // exactly "foo.bar"
60
- IgnoringTopFunction("foo.bar...") // top function name with prefix "foo.bar." (note the trailing dot!)
61
- IgnoringTopFunction("foo.bar [chan receive]") // exactly "foo.bar" with state starting with "chan receive"
62
- IgnoringGoroutines(expectedGoroutines) // ignore specified goroutines with these IDs
63
- IgnoringInBacktrace("foo.bar.baz") // "foo.bar.baz" within the backtrace
64
- IgnoringCreator("foo.bar") // exact creator function name "foo.bar"
65
- IgnoringCreator("foo.bar...") // creator function name with prefix "foo.bar."
58
+ IgnoringTopFunction("foo.bar") // exactly "foo.bar"
59
+ IgnoringTopFunction("foo.bar...") // top function name with prefix "foo.bar." (note the trailing dot!)
60
+ IgnoringTopFunction("foo.bar [chan receive]") // exactly "foo.bar" with state starting with "chan receive"
61
+ IgnoringGoroutines(expectedGoroutines) // ignore specified goroutines with these IDs
62
+ IgnoringInBacktrace("foo.bar.baz") // "foo.bar.baz" within the backtrace
63
+ IgnoringCreator("foo.bar") // exact creator function name "foo.bar"
64
+ IgnoringCreator("foo.bar...") // creator function name with prefix "foo.bar."
66
65
67
66
In addition, you can use any other GomegaMatcher, as long as it can work on a
68
67
(single) Goroutine. For instance, Gomega's HaveField and WithTransform
69
68
matchers are good foundations for writing project-specific gleak matchers.
70
69
71
- Leaked Goroutine Dump
70
+ # Leaked Goroutine Dump
72
71
73
72
By default, when gleak's HaveLeaked matcher finds one or more leaked
74
73
goroutines, it dumps the goroutine backtraces in a condensed format that uses
75
74
only a single line per call instead of two lines. Moreover, the backtraces
76
75
abbreviate the source file location in the form of package/source.go:lineno:
77
76
78
- goroutine 42 [flabbergasted]
79
- main.foo.func1() at foo/test.go:6
80
- created by main.foo at foo/test.go:5
77
+ goroutine 42 [flabbergasted]
78
+ main.foo.func1() at foo/test.go:6
79
+ created by main.foo at foo/test.go:5
81
80
82
81
By setting gleak.ReportFilenameWithPath=true the leaky goroutine backtraces
83
82
will show full path names for each source file:
84
83
85
- goroutine 42 [flabbergasted]
86
- main.foo.func1() at /home/go/foo/test.go:6
87
- created by main.foo at home/go/foo/test.go:5
84
+ goroutine 42 [flabbergasted]
85
+ main.foo.func1() at /home/go/foo/test.go:6
86
+ created by main.foo at home/go/foo/test.go:5
88
87
89
- Acknowledgement
88
+ # Acknowledgement
90
89
91
90
gleak has been heavily inspired by the Goroutine leak detector
92
91
github.com/uber-go/goleak. That's definitely a fine piece of work!
@@ -110,9 +109,8 @@ out the non-leaking (and therefore expected) goroutines, using a few filter
110
109
criteria. That is, a few new goroutine-related matchers. In this architecture,
111
110
even existing Gomega matchers can optionally be (re)used as the need arises.
112
111
113
- References
112
+ # References
114
113
115
114
https://github.com/onsi/gomega and https://github.com/onsi/ginkgo.
116
-
117
115
*/
118
116
package gleak
0 commit comments