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

add fixenv.RunTests #36

Merged
merged 1 commit into from
Nov 26, 2023
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion env.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,8 @@ func (e *EnvT) fixtureCallWrapper(key cacheKey, f FixtureCallbackFunc, opt *Fixt
e.m.Unlock()

if si == nil {
e.t.Fatalf("Unexpected scope. Create env for test %q", scopeName)
e.t.Fatalf("Unexpected scope: %q. Initialize package scope before use."+
"For scope %s use fixenv.RunTests", scopeName, packageScopeName)
// not reachable
return nil, nil
}
Expand Down
41 changes: 41 additions & 0 deletions examples/simple_main_test/example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//go:build go1.18
// +build go1.18

package simple_main_test

import (
"github.com/rekby/fixenv"
"math/rand"
"testing"
)

var global int = -1

func FSingleRandom(e fixenv.Env) int {
var f fixenv.GenericFixtureFunction[int] = func() (*fixenv.GenericResult[int], error) {
return fixenv.NewGenericResult(rand.Int()), nil
}
return fixenv.CacheResult(e, f, fixenv.CacheOptions{Scope: fixenv.ScopePackage})
}

func TestFirst(t *testing.T) {
e := fixenv.New(t)
if global == -1 {
global = FSingleRandom(e)
}

if singleRnd := FSingleRandom(e); singleRnd != global {
t.Fatalf("%v != %v", singleRnd, global)
}
}

func TestSecond(t *testing.T) {
e := fixenv.New(t)
if global == -1 {
global = FSingleRandom(e)
}

if singleRnd := FSingleRandom(e); singleRnd != global {
t.Fatalf("%v != %v", singleRnd, global)
}
}
11 changes: 11 additions & 0 deletions examples/simple_main_test/testmain_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package simple_main

import (
"github.com/rekby/fixenv"
"os"
"testing"
)

func TestMain(m *testing.M) {
os.Exit(fixenv.RunTests(m))
}
37 changes: 37 additions & 0 deletions maintest.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package fixenv

import (
"errors"
"fmt"
"log"
"sync"
)

var errTooManyOptionalArgs = errors.New("allow not more then one optional arg")

// FatalfFunction function signature of Fatalf
type FatalfFunction func(format string, args ...interface{})

Expand Down Expand Up @@ -34,18 +37,52 @@ type CreateMainTestEnvOpts struct {
SkipNow SkipNowFunction
}

// packageLevelVirtualTest now used for tests only
var lastPackageLevelVirtualTest *virtualTest

// CreateMainTestEnv called from TestMain for create global environment.
// It need only for use ScopePackage cache scope.
// If ScopePackage not used - no need to create main env.
func CreateMainTestEnv(opts *CreateMainTestEnvOpts) (env *EnvT, tearDown func()) {
// TODO: handle second time initialize
globalMutex.Lock()
packageLevelVirtualTest := newVirtualTest(opts)
lastPackageLevelVirtualTest = packageLevelVirtualTest
globalMutex.Unlock()

env = New(packageLevelVirtualTest) // register global test for env
return env, packageLevelVirtualTest.cleanup
}

// RunTests runs the tests. It returns an exit code to pass to os.Exit.
//
// Usage:
// declare in _test file TestMain function:
//
// func TestMain(m *testing.M) {
// os.Exit(fixenv.RunTests(m))
// }
func RunTests(m RunTestsI, opts ...CreateMainTestEnvOpts) int {
var options *CreateMainTestEnvOpts
switch len(opts) {
case 0:
// pass
case 1:
options = &opts[0]
default:
panic(errTooManyOptionalArgs)
}

_, cancel := CreateMainTestEnv(options)
defer cancel()
return m.Run()
}

type RunTestsI interface {
// Run runs the tests. It returns an exit code to pass to os.Exit.
Run() (code int)
}

// virtualTest implement T interface for global env scope
type virtualTest struct {
m sync.Mutex
Expand Down
87 changes: 87 additions & 0 deletions maintest_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package fixenv

import (
"errors"
"runtime"
"sync"
"testing"
Expand Down Expand Up @@ -76,3 +77,89 @@ func TestCreateMainTestEnv(t *testing.T) {
})
})
}

func TestRunTests(t *testing.T) {
expectedReturnCode := 123

checkInitialized := func(t *testing.T) {
t.Helper()

globalMutex.Lock()
defer globalMutex.Unlock()

if _, ok := globalScopeInfo[packageScopeName]; !ok {
t.Fatal()
}
}
cleanGlobalState := func() {
globalMutex.Lock()
defer globalMutex.Unlock()

delete(globalScopeInfo, packageScopeName)
}

t.Run("without options", func(t *testing.T) {
m := &mTestsMock{
returnCode: expectedReturnCode,
run: func() {
checkInitialized(t)
},
}

if res := RunTests(m); res != expectedReturnCode {
t.Fatalf("%v != %v", res, expectedReturnCode)
}
cleanGlobalState()
})
t.Run("with options", func(t *testing.T) {
m := &mTestsMock{
returnCode: expectedReturnCode,
run: func() {
checkInitialized(t)
lastPackageLevelVirtualTest.SkipNow()
},
}

called := false
RunTests(m, CreateMainTestEnvOpts{SkipNow: func() {
called = true
}})
if !called {
t.Fatal()
}
cleanGlobalState()
})
t.Run("with two options", func(t *testing.T) {
defer func() {
cleanGlobalState()

rec := recover()
if !errors.Is(rec.(error), errTooManyOptionalArgs) {
t.Fatal(rec)
}
}()
m := &mTestsMock{
run: func() {
checkInitialized(t)
},
}
RunTests(m, CreateMainTestEnvOpts{}, CreateMainTestEnvOpts{})
})
}

type mTestsMock struct {
runCalled bool
returnCode int
run func()
}

func (r *mTestsMock) Run() (code int) {
r.runCalled = true
if r.run != nil {
r.run()
}
return r.returnCode
}

// check interface implementation
var _ RunTestsI = &mTestsMock{}
Loading