Skip to content

Commit

Permalink
export Goroutine type alias for convenience; update documentation (#558)
Browse files Browse the repository at this point in the history
  • Loading branch information
thediveo committed Jun 13, 2022
1 parent f5a83b1 commit 293dd88
Show file tree
Hide file tree
Showing 12 changed files with 71 additions and 78 deletions.
12 changes: 6 additions & 6 deletions docs/index.md
Expand Up @@ -3113,7 +3113,7 @@ in form of `GomegaMatcher`s or in shorthand notation:
function on the backtrace stack has the exact name `foo.bar` _and_ the
goroutine is in a state beginning with `chan receive`.

- `[]goroutine.Goroutine` is shorthand for
- `[]Goroutine` is shorthand for
`IgnoringGoroutines(<SLICEOFGOROUTINES>)`: it filters out the specified
goroutines, considering them to be non-leaky. The goroutines are identified by
their [goroutine IDs](#goroutine-ids).
Expand All @@ -3123,7 +3123,7 @@ in form of `GomegaMatcher`s or in shorthand notation:

- additionally, any other `GomegaMatcher` can be passed to `HaveLeaked()`, as
long as this matcher can work on a passed-in actual value of type
`goroutine.Goroutine`.
`Goroutine`.

### Goroutine Matchers

Expand Down Expand Up @@ -3151,9 +3151,9 @@ criteria:
- `"foo.bar [state]"` matches if a goroutine's topmost function has this exact
name and the goroutine's state begins with the specified state string.

`ACTUAL` must be an array or slice of `goroutine.Goroutine`s.
`ACTUAL` must be an array or slice of `Goroutine`s.

#### IgnoringGoroutines(goroutines []goroutine.Goroutine)
#### IgnoringGoroutines(goroutines []Goroutine)

```go
Eventually(ACTUAL).ShouldNot(HaveLeaked(IgnoringGoroutines(GOROUTINES)))
Expand All @@ -3164,7 +3164,7 @@ are elements of `GOROUTINES`, causing `HaveLeaked` to filter out the matched
goroutine(s) as non-leaky. `IgnoringGoroutines` compares goroutines by their
`ID`s (see [Goroutine IDs](#gorotuine-ids) for background information).

`ACTUAL` must be an array or slice of `goroutine.Goroutine`s.
`ACTUAL` must be an array or slice of `Goroutine`s.

#### IgnoringInBacktrace(fname string)

Expand All @@ -3178,7 +3178,7 @@ filter out the matched goroutine as non-leaky. Please note that
`IgnoringInBacktrace` uses a (somewhat lazy) `strings.Contains` to check for any
occurence of `FNAME` in backtraces.

`ACTUAL` must be an array or slice of `goroutine.Goroutine`s.
`ACTUAL` must be an array or slice of `Goroutine`s.

#### IgnoringCreator(creatorname string)

Expand Down
4 changes: 2 additions & 2 deletions gleak/doc.go
Expand Up @@ -26,7 +26,7 @@ because no one wants leaked goroutines.
A typical pattern to detect goroutines leaked in individual tests is as follows:
var ignoreGood []goroutine.Goroutine
var ignoreGood []Goroutine
BeforeEach(func() {
ignoreGood = Goroutines()
Expand Down Expand Up @@ -65,7 +65,7 @@ arguments to HaveLeaked(...):
IgnoringCreator("foo.bar...") // creator function name with prefix "foo.bar."
In addition, you can use any other GomegaMatcher, as long as it can work on a
(single) goroutine.Goroutine. For instance, Gomega's HaveField and WithTransform
(single) Goroutine. For instance, Gomega's HaveField and WithTransform
matchers are good foundations for writing project-specific gleak matchers.
Leaked Goroutine Dump
Expand Down
6 changes: 5 additions & 1 deletion gleak/goroutines.go
Expand Up @@ -2,9 +2,13 @@ package gleak

import "github.com/onsi/gomega/gleak/goroutine"

// Goroutine represents information about a single goroutine and is a
// convenience type alias.
type Goroutine = goroutine.Goroutine

// Goroutines returns information about all goroutines: their goroutine IDs, the
// names of the topmost functions in the backtraces, and finally the goroutine
// backtraces.
func Goroutines() []goroutine.Goroutine {
func Goroutines() []Goroutine {
return goroutine.Goroutines()
}
18 changes: 9 additions & 9 deletions gleak/have_leaked_matcher.go
Expand Up @@ -127,7 +127,7 @@ func HaveLeaked(ignoring ...interface{}) types.GomegaMatcher {
switch ign := ign.(type) {
case string:
m.filters = append(m.filters, IgnoringTopFunction(ign))
case []goroutine.Goroutine:
case []Goroutine:
m.filters = append(m.filters, IgnoringGoroutines(ign))
case types.GomegaMatcher:
m.filters = append(m.filters, ign)
Expand All @@ -143,12 +143,12 @@ func HaveLeaked(ignoring ...interface{}) types.GomegaMatcher {
// goroutines.
type HaveLeakedMatcher struct {
filters []types.GomegaMatcher // expected goroutines that aren't leaks.
leaked []goroutine.Goroutine // surplus goroutines which we consider to be leaks.
leaked []Goroutine // surplus goroutines which we consider to be leaks.
}

var gsT = reflect.TypeOf([]goroutine.Goroutine{})
var gsT = reflect.TypeOf([]Goroutine{})

// Match succeeds if actual is an array or slice of goroutine.Goroutine
// Match succeeds if actual is an array or slice of Goroutine
// information and still contains goroutines after filtering out all expected
// goroutines that were specified when creating the matcher.
func (matcher *HaveLeakedMatcher) Match(actual interface{}) (success bool, err error) {
Expand All @@ -165,7 +165,7 @@ func (matcher *HaveLeakedMatcher) Match(actual interface{}) (success bool, err e
"HaveLeaked matcher expects an array or slice of goroutines. Got:\n%s",
format.Object(actual, 1))
}
goroutines := val.Convert(gsT).Interface().([]goroutine.Goroutine)
goroutines := val.Convert(gsT).Interface().([]Goroutine)
matcher.leaked, err = matcher.filter(goroutines, matcher.filters)
if err != nil {
return false, err
Expand All @@ -189,7 +189,7 @@ func (matcher *HaveLeakedMatcher) NegatedFailureMessage(actual interface{}) (mes
// listGoroutines returns a somewhat compact textual representation of the
// specified goroutines, by ignoring the often quite lengthy backtrace
// information.
func (matcher *HaveLeakedMatcher) listGoroutines(gs []goroutine.Goroutine, indentation uint) string {
func (matcher *HaveLeakedMatcher) listGoroutines(gs []Goroutine, indentation uint) string {
var buff strings.Builder
indent := strings.Repeat(format.Indent, int(indentation))
backtraceIdent := strings.Repeat(format.Indent, int(indentation+1))
Expand Down Expand Up @@ -261,9 +261,9 @@ func (matcher *HaveLeakedMatcher) listGoroutines(gs []goroutine.Goroutine, inden
// all checkers do not signal that they expect a certain goroutine then this
// goroutine is considered to be a leak.
func (matcher *HaveLeakedMatcher) filter(
goroutines []goroutine.Goroutine, filters []types.GomegaMatcher,
) ([]goroutine.Goroutine, error) {
gs := make([]goroutine.Goroutine, 0, len(goroutines))
goroutines []Goroutine, filters []types.GomegaMatcher,
) ([]Goroutine, error) {
gs := make([]Goroutine, 0, len(goroutines))
myID := goroutine.Current().ID
nextgoroutine:
for _, g := range goroutines {
Expand Down
20 changes: 9 additions & 11 deletions gleak/have_leaked_matcher_test.go
Expand Up @@ -6,8 +6,6 @@ import (
"sync"
"time"

"github.com/onsi/gomega/gleak/goroutine"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
Expand All @@ -18,7 +16,7 @@ import (
var _ = Describe("HaveLeaked", func() {

It("renders indented goroutine information including (malformed) backtrace", func() {
gs := []goroutine.Goroutine{
gs := []Goroutine{
{
ID: 42,
State: "stoned",
Expand All @@ -34,7 +32,7 @@ created by main.foo
main.foo.func1() at foo/test.go:6
created by main.foo at foo/test.go:5`))

gs = []goroutine.Goroutine{
gs = []Goroutine{
{
ID: 42,
State: "stoned",
Expand All @@ -48,7 +46,7 @@ created by main.foo
main.foo.func1() at foo/test.go:6
created by main.foo at foo/test.go:5`))

gs = []goroutine.Goroutine{
gs = []Goroutine{
{
ID: 42,
State: "stoned",
Expand All @@ -62,7 +60,7 @@ created by main.foo
main.foo.func1() at foo/test.go:6
created by main.foo at foo/test.go:5`))

gs = []goroutine.Goroutine{
gs = []Goroutine{
{
ID: 42,
State: "stoned",
Expand Down Expand Up @@ -121,7 +119,7 @@ created by main.foo`,

Context("failure messages", func() {

var snapshot []goroutine.Goroutine
var snapshot []Goroutine

BeforeEach(func() {
snapshot = Goroutines()
Expand Down Expand Up @@ -164,12 +162,12 @@ created by main.foo`,

It("accepts plain strings as filters", func() {
m := HaveLeaked("foo.bar")
Expect(m.Match([]goroutine.Goroutine{
Expect(m.Match([]Goroutine{
{TopFunction: "foo.bar"},
})).To(BeFalse())
})

It("expects actual to be a slice of goroutine.Goroutine", func() {
It("expects actual to be a slice of Goroutine", func() {
m := HaveLeaked()
Expect(m.Match(nil)).Error().To(MatchError(
"HaveLeaked matcher expects an array or slice of goroutines. Got:\n <nil>: nil"))
Expand All @@ -181,7 +179,7 @@ created by main.foo`,

It("handles filter matcher errors", func() {
m := HaveLeaked(HaveField("foobar", BeNil()))
Expect(m.Match([]goroutine.Goroutine{
Expect(m.Match([]Goroutine{
{ID: 0},
})).Error().To(HaveOccurred())
})
Expand All @@ -192,7 +190,7 @@ created by main.foo`,

Context("wrapped around test nodes", func() {

var snapshot []goroutine.Goroutine
var snapshot []Goroutine

When("not leaking", func() {

Expand Down
14 changes: 7 additions & 7 deletions gleak/ignoring_creator_test.go
Expand Up @@ -9,8 +9,8 @@ import (
. "github.com/onsi/gomega"
)

func creator() goroutine.Goroutine {
ch := make(chan goroutine.Goroutine)
func creator() Goroutine {
ch := make(chan Goroutine)
go func() {
ch <- goroutine.Current()
}()
Expand All @@ -21,7 +21,7 @@ var _ = Describe("IgnoringCreator matcher", func() {

It("returns an error for an invalid actual", func() {
m := IgnoringCreator("foo.bar")
Expect(m.Match(nil)).Error().To(MatchError("IgnoringCreator matcher expects a goroutine.Goroutine or *goroutine.Goroutine. Got:\n <nil>: nil"))
Expect(m.Match(nil)).Error().To(MatchError("IgnoringCreator matcher expects a Goroutine or *Goroutine. Got:\n <nil>: nil"))
})

It("matches a creator function by full name", func() {
Expand All @@ -40,20 +40,20 @@ var _ = Describe("IgnoringCreator matcher", func() {
g := creator()
Expect(m.Match(g)).To(BeTrue(), "creator %v", g.String())
Expect(m.Match(goroutine.Current())).To(BeFalse())
Expect(m.Match(goroutine.Goroutine{
Expect(m.Match(Goroutine{
TopFunction: "spanish.inquisition",
})).To(BeFalse())
})

It("returns failure messages", func() {
m := IgnoringCreator("foo.bar")
Expect(m.FailureMessage(goroutine.Goroutine{ID: 42, TopFunction: "foo"})).To(Equal(
Expect(m.FailureMessage(Goroutine{ID: 42, TopFunction: "foo"})).To(Equal(
"Expected\n <goroutine.Goroutine>: {ID: 42, State: \"\", TopFunction: \"foo\", CreatorFunction: \"\", BornAt: \"\"}\nto be created by \"foo.bar\""))
Expect(m.NegatedFailureMessage(goroutine.Goroutine{ID: 42, TopFunction: "foo"})).To(Equal(
Expect(m.NegatedFailureMessage(Goroutine{ID: 42, TopFunction: "foo"})).To(Equal(
"Expected\n <goroutine.Goroutine>: {ID: 42, State: \"\", TopFunction: \"foo\", CreatorFunction: \"\", BornAt: \"\"}\nnot to be created by \"foo.bar\""))

m = IgnoringCreator("foo...")
Expect(m.FailureMessage(goroutine.Goroutine{ID: 42, TopFunction: "foo"})).To(Equal(
Expect(m.FailureMessage(Goroutine{ID: 42, TopFunction: "foo"})).To(Equal(
"Expected\n <goroutine.Goroutine>: {ID: 42, State: \"\", TopFunction: \"foo\", CreatorFunction: \"\", BornAt: \"\"}\nto be created by a function with prefix \"foo.\""))
})

Expand Down
6 changes: 2 additions & 4 deletions gleak/ignoring_goroutines.go
Expand Up @@ -3,8 +3,6 @@ package gleak
import (
"sort"

"github.com/onsi/gomega/gleak/goroutine"

"github.com/onsi/gomega/format"
"github.com/onsi/gomega/types"
)
Expand All @@ -14,7 +12,7 @@ import (
// matcher is to take a snapshot of the current goroutines just right before a
// test and then at the end of a test filtering out these "good" and known
// goroutines.
func IgnoringGoroutines(goroutines []goroutine.Goroutine) types.GomegaMatcher {
func IgnoringGoroutines(goroutines []Goroutine) types.GomegaMatcher {
m := &ignoringGoroutinesMatcher{
ignoreGoids: map[uint64]struct{}{},
}
Expand All @@ -28,7 +26,7 @@ type ignoringGoroutinesMatcher struct {
ignoreGoids map[uint64]struct{}
}

// Match succeeds if actual is a goroutine.Goroutine and its ID is in the set of
// Match succeeds if actual is a Goroutine and its ID is in the set of
// goroutine IDs to expect and thus to ignore in leak checks.
func (matcher *ignoringGoroutinesMatcher) Match(actual interface{}) (success bool, err error) {
g, err := G(actual, "IgnoringGoroutines")
Expand Down
10 changes: 4 additions & 6 deletions gleak/ignoring_goroutines_test.go
@@ -1,8 +1,6 @@
package gleak

import (
"github.com/onsi/gomega/gleak/goroutine"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
Expand All @@ -12,7 +10,7 @@ var _ = Describe("IgnoringGoroutines matcher", func() {
It("returns an error for an invalid actual", func() {
m := IgnoringGoroutines(Goroutines())
Expect(m.Match(nil)).Error().To(MatchError(
"IgnoringGoroutines matcher expects a goroutine.Goroutine or *goroutine.Goroutine. Got:\n <nil>: nil"))
"IgnoringGoroutines matcher expects a Goroutine or *Goroutine. Got:\n <nil>: nil"))
})

It("matches", func() {
Expand All @@ -21,14 +19,14 @@ var _ = Describe("IgnoringGoroutines matcher", func() {
m := IgnoringGoroutines(gs)
Expect(m.Match(me)).To(BeTrue())
Expect(m.Match(gs[1])).To(BeTrue())
Expect(m.Match(goroutine.Goroutine{})).To(BeFalse())
Expect(m.Match(Goroutine{})).To(BeFalse())
})

It("returns failure messages", func() {
m := IgnoringGoroutines(Goroutines())
Expect(m.FailureMessage(goroutine.Goroutine{})).To(MatchRegexp(
Expect(m.FailureMessage(Goroutine{})).To(MatchRegexp(
`Expected\n <goroutine.Goroutine>: {ID: 0, State: "", TopFunction: "", CreatorFunction: "", BornAt: ""}\nto be contained in the list of expected goroutine IDs\n <\[\]uint64 | len:\d+, cap:\d+>: [.*]`))
Expect(m.NegatedFailureMessage(goroutine.Goroutine{})).To(MatchRegexp(
Expect(m.NegatedFailureMessage(Goroutine{})).To(MatchRegexp(
`Expected\n <goroutine.Goroutine>: {ID: 0, State: "", TopFunction: "", CreatorFunction: "", BornAt: ""}\nnot to be contained in the list of expected goroutine IDs\n <\[\]uint64 | len:\d+, cap:\d+>: [.*]`))
})

Expand Down
8 changes: 4 additions & 4 deletions gleak/ignoring_in_backtrace_test.go
Expand Up @@ -14,7 +14,7 @@ var _ = Describe("IgnoringInBacktrace matcher", func() {
It("returns an error for an invalid actual", func() {
m := IgnoringInBacktrace("foo.bar")
Expect(m.Match(nil)).Error().To(MatchError(
"IgnoringInBacktrace matcher expects a goroutine.Goroutine or *goroutine.Goroutine. Got:\n <nil>: nil"))
"IgnoringInBacktrace matcher expects a Goroutine or *Goroutine. Got:\n <nil>: nil"))
})

It("matches", func() {
Expand All @@ -26,14 +26,14 @@ var _ = Describe("IgnoringInBacktrace matcher", func() {

It("returns failure messages", func() {
m := IgnoringInBacktrace("foo.bar")
Expect(m.FailureMessage(goroutine.Goroutine{Backtrace: "abc"})).To(MatchRegexp(
Expect(m.FailureMessage(Goroutine{Backtrace: "abc"})).To(MatchRegexp(
`Expected\n <goroutine.Goroutine>: {ID: 0, State: "", TopFunction: "", CreatorFunction: "", BornAt: ""}\nto contain "foo.bar" in the goroutine's backtrace`))
Expect(m.NegatedFailureMessage(goroutine.Goroutine{Backtrace: "abc"})).To(MatchRegexp(
Expect(m.NegatedFailureMessage(Goroutine{Backtrace: "abc"})).To(MatchRegexp(
`Expected\n <goroutine.Goroutine>: {ID: 0, State: "", TopFunction: "", CreatorFunction: "", BornAt: ""}\nnot to contain "foo.bar" in the goroutine's backtrace`))
})

})

func somefunction() goroutine.Goroutine {
func somefunction() Goroutine {
return goroutine.Current()
}

0 comments on commit 293dd88

Please sign in to comment.