Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[gleak] export Goroutine type alias for convenience; update documentation #558

Merged
merged 1 commit into from Jun 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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()
}