Skip to content

Commit

Permalink
feat: improve handling of repeated parallel test setup (#60)
Browse files Browse the repository at this point in the history
Signed-off-by: tkrop <[email protected]>
  • Loading branch information
tkrop committed Dec 16, 2023
1 parent 3f3c666 commit b61accb
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 9 deletions.
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ endif
# request targets, while the single target can be used to define the
# precondition of custom target.
.PHONY: $(TARGETS) $(addprefix target/,$(TARGETS))
$(TARGETS):; $(GOBIN)/go-make $(MAKEFLAGS) $(MAKECMDGOALS);
$(eval $(lastwords $(MAKECMDGOALS)):;@:)
$(firstword $(MAKECMDGOALS)):
$(GOBIN)/go-make $(MAKEFLAGS) $(MAKECMDGOALS);
$(addprefix target/,$(TARGETS)): target/%:
$(GOBIN)/go-make $(MAKEFLAGS) $*;

Expand Down
5 changes: 5 additions & 0 deletions internal/mock/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,11 @@ var (
Params: []*Param{},
Results: []*Param{{Type: "string"}},
Variadic: false,
}, {
Name: "Parallel",
Params: []*Param{},
Results: []*Param{},
Variadic: false,
}, {
Name: "TempDir",
Params: []*Param{},
Expand Down
2 changes: 1 addition & 1 deletion test/caller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ var (
}()
// CallerTestErrorf provides the file with the line number of the `Errorf`
// call in testing.
CallerTestErrorf = path.Join(SourceDir, "testing.go:180")
CallerTestErrorf = path.Join(SourceDir, "testing.go:211")
// CallerGomockErrorf provides the file with the line number of the
// `Errorf` call in gomock.
CallerGomockErrorf = path.Join(SourceDir, "gomock.go:61")
Expand Down
49 changes: 42 additions & 7 deletions test/testing.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,18 @@ const (
Parallel = true
)

// ensureParallel ensures that the test runs test parameter sets in parallel.
func ensureParallel(t *testing.T) {
t.Helper()
defer func() {
if v := recover(); v != nil &&
v != "testing: t.Parallel called multiple times" {
panic(v)
}
}()
t.Parallel()
}

// TODO: consider following convenience methods:
//
// // Result is a convenience method that returns the first argument ans swollows
Expand Down Expand Up @@ -87,12 +99,31 @@ type Reporter interface {
// Test is a minimal interface for abstracting test methods that are needed to
// setup an isolated test environment for GoMock and Testify.
type Test interface {
// Helper declares a test helper function. The method delegates the request
// to the parent test context.
Helper()
// Name provides the test name. The method delegates the request to the
// parent test context.
Name() string
// TempDir creates a new temporary directory for the test. The method
// delegates the request to the parent test context.
TempDir() string
// Errorf handles a failure messages when a test is supposed to continue.
// The method either delegates the request to the parent test context or
// to the test reporter.
Errorf(format string, args ...any)
// Fatalf handles a fatal failure messge that immediate aborts of the test
// execution. The method either delegates the request to the parent test
// context or to the test reporter.
Fatalf(format string, args ...any)
// FailNow handles fatal failure notifications without log output that
// aborts test execution immediately. The method either delegates the
// request to the parent test context or to the test reporter.
FailNow()
// Parallel declares that the test is to be run in parallel with (and only
// with) other parallel tests. The method delegates the request to the
// parent test context and silently recovers when setting up twice.
Parallel()
}

// Cleanuper defines an interface to add a custom mehtod that is called after
Expand Down Expand Up @@ -127,7 +158,7 @@ func NewTester(t Test, expect Expect) *Tester {
// Parallel delegates request to `testing.T.Parallel()`.
func (t *Tester) Parallel() {
if t, ok := t.t.(*testing.T); ok {
t.Parallel()
ensureParallel(t)
}
}

Expand Down Expand Up @@ -391,15 +422,21 @@ func (r *runner[P]) RunSeq(call func(t Test, param P)) Runner[P] {
return r.run(call, false)
}

// parallel ensures that the test runner runs the test parameter sets in
// parallel.
func (r *runner[P]) parallel(parallel bool) {
if parallel {
ensureParallel(r.t)
}
}

// Run runs the test parameter sets either parallel or in sequence.
func (r *runner[P]) run(
call func(t Test, param P), parallel bool,
) Runner[P] {
switch params := r.params.(type) {
case map[string]P:
if parallel {
r.t.Parallel()
}
r.parallel(parallel)
r.wg.Add(len(params))

for name, param := range params {
Expand All @@ -408,9 +445,7 @@ func (r *runner[P]) run(
}

case []P:
if parallel {
r.t.Parallel()
}
r.parallel(parallel)
r.wg.Add(len(params))

for index, param := range params {
Expand Down
19 changes: 19 additions & 0 deletions test/testing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,3 +280,22 @@ func TestTypePanic(t *testing.T) {
test.New[TestParam](t, ParamParam{expect: false}).
Run(func(t test.Test, param TestParam) {})
}

func TestParallel(t *testing.T) {
t.Parallel()
test.New[ParamParam](t, []ParamParam{{expect: false}}).
Run(func(t test.Test, param ParamParam) {
t.Parallel()
})
}

func TestParallelDenied(t *testing.T) {
t.Setenv("TESTING", "true")
defer func() {
assert.Equal(t, "testing: t.Parallel called after t.Setenv;"+
" cannot set environment variables in parallel tests", recover())
}()

test.New[ParamParam](t, []ParamParam{{expect: false}}).
Run(func(t test.Test, param ParamParam) {})
}

0 comments on commit b61accb

Please sign in to comment.