diff --git a/env.go b/env.go index 37f71f1..e66c7ca 100644 --- a/env.go +++ b/env.go @@ -83,6 +83,8 @@ func (e *EnvT) T() T { // opt - fixture options, nil for default options. // f - callback - fixture body. // Cache guarantee for call f exactly once for same Cache called and params combination. +// Deprecated: will be removed in next versions. +// Use EnvT.CacheResult instead func (e *EnvT) Cache(cacheKey interface{}, opt *FixtureOptions, f FixtureCallbackFunc) interface{} { return e.cache(cacheKey, opt, f) } @@ -99,6 +101,8 @@ func (e *EnvT) Cache(cacheKey interface{}, opt *FixtureOptions, f FixtureCallbac // f - callback - fixture body. // cleanup, returned from f called while fixture cleanup // Cache guarantee for call f exactly once for same Cache called and params combination. +// Deprecated: will be removed in next versions. +// Use EnvT.CacheResult instead func (e *EnvT) CacheWithCleanup(cacheKey interface{}, opt *FixtureOptions, f FixtureCallbackWithCleanupFunc) interface{} { if opt == nil { opt = &FixtureOptions{} @@ -119,6 +123,43 @@ func (e *EnvT) CacheWithCleanup(cacheKey interface{}, opt *FixtureOptions, f Fix return e.cache(cacheKey, opt, fWithoutCleanup) } +// CacheResult call f callback once and cache result (ok and error), +// then return same result for all calls of the callback without additional calls +// f with same options calls max once per test (or defined test scope) +func (e *EnvT) CacheResult(f FixtureFunction, options ...CacheOptions) interface{} { + var cacheOptions CacheOptions + switch len(options) { + case 0: + cacheOptions = CacheOptions{} + case 1: + cacheOptions = options[0] + default: + panic(fmt.Errorf("max len of cache result cacheOptions is 1, given: %v", len(options))) + } + + var resCleanupFunc FixtureCleanupFunc + + var fWithoutCleanup FixtureCallbackFunc = func() (res interface{}, err error) { + result, err := f() + resCleanupFunc = result.Cleanup + return result.Value, err + } + + opt := &FixtureOptions{} + opt.Scope = cacheOptions.Scope + opt.additionlSkipExternalCalls = cacheOptions.additionlSkipExternalCalls + opt.cleanupFunc = resCleanupFunc + + opt.cleanupFunc = func() { + if resCleanupFunc != nil { + resCleanupFunc() + } + } + + return e.cache(cacheOptions.CacheKey, opt, fWithoutCleanup) + +} + // cache must be call from first-level public function // UserFunction->EnvFunction->cache for good determine caller name func (e *EnvT) cache(cacheKey interface{}, opt *FixtureOptions, f FixtureCallbackFunc) interface{} { diff --git a/env_generic_sugar.go b/env_generic_sugar.go index 09e0a0e..0594e9a 100644 --- a/env_generic_sugar.go +++ b/env_generic_sugar.go @@ -3,6 +3,11 @@ package fixenv +import "fmt" + +// Cache is call f once per cache scope (default per test) and cache result (success or error). +// All other calls of the f will return same result +// Deprecated: Use CacheResult func Cache[TRes any](env Env, cacheKey any, opt *FixtureOptions, f func() (TRes, error)) TRes { addSkipLevel(&opt) callbackResult := env.Cache(cacheKey, opt, func() (res interface{}, err error) { @@ -16,6 +21,10 @@ func Cache[TRes any](env Env, cacheKey any, opt *FixtureOptions, f func() (TRes, return res } +// CacheWithCleanup is call f once per cache scope (default per test) and cache result (success or error). +// All other calls of the f will return same result. +// Used when fixture need own cleanup after exit from test scope +// Deprecated: Use CacheResult func CacheWithCleanup[TRes any](env Env, cacheKey any, opt *FixtureOptions, f func() (TRes, FixtureCleanupFunc, error)) TRes { addSkipLevel(&opt) callbackResult := env.CacheWithCleanup(cacheKey, opt, func() (res interface{}, cleanup FixtureCleanupFunc, err error) { @@ -29,9 +38,62 @@ func CacheWithCleanup[TRes any](env Env, cacheKey any, opt *FixtureOptions, f fu return res } +// CacheResult is call f once per cache scope (default per test) and cache result (success or error). +// All other calls of the f will return same result. +func CacheResult[TRes any](env Env, f GenericFixtureFunction[TRes], options ...CacheOptions) TRes { + var cacheOptions CacheOptions + switch len(options) { + case 0: + cacheOptions = CacheOptions{} + case 1: + cacheOptions = options[0] + default: + panic(fmt.Errorf("max len of cache result cacheOptions is 1, given: %v", len(options))) + } + + addSkipLevelCache(&cacheOptions) + var oldStyleFunc FixtureFunction = func() (*Result, error) { + res, err := f() + + var oldStyleRes *Result + if res != nil { + oldStyleRes = &Result{ + Value: res.Value, + ResultAdditional: res.ResultAdditional, + } + } + return oldStyleRes, err + } + res := env.CacheResult(oldStyleFunc, cacheOptions) + return res.(TRes) +} + +// GenericFixtureFunction - callback function with structured result +type GenericFixtureFunction[ResT any] func() (*GenericResult[ResT], error) + +// GenericResult of fixture callback +type GenericResult[ResT any] struct { + Value ResT + ResultAdditional +} + +// NewGenericResult return result struct and nil error. +// Use it for smaller boilerplate for define generic specifications +func NewGenericResult[ResT any](res ResT) *GenericResult[ResT] { + return &GenericResult[ResT]{Value: res} +} + +func NewGenericResultWithCleanup[ResT any](res ResT, cleanup FixtureCleanupFunc) *GenericResult[ResT] { + return &GenericResult[ResT]{Value: res, ResultAdditional: ResultAdditional{Cleanup: cleanup}} +} + func addSkipLevel(optspp **FixtureOptions) { if *optspp == nil { *optspp = &FixtureOptions{} } (*optspp).additionlSkipExternalCalls++ } + +func addSkipLevelCache(optspp *CacheOptions) { + (*optspp).additionlSkipExternalCalls++ +} diff --git a/env_generic_sugar_test.go b/env_generic_sugar_test.go index dfa3f31..78a826e 100644 --- a/env_generic_sugar_test.go +++ b/env_generic_sugar_test.go @@ -4,7 +4,10 @@ package fixenv import ( + "fmt" "github.com/rekby/fixenv/internal" + "github.com/stretchr/testify/assert" + "math/rand" "testing" "github.com/stretchr/testify/require" @@ -89,12 +92,108 @@ func TestCacheWithCleanupGeneric(t *testing.T) { require.Equal(t, 1, f1()) require.Equal(t, 2, f2()) }) +} + +func TestCacheResultGeneric(t *testing.T) { + t.Run("PassParams", func(t *testing.T) { + inOpt := CacheOptions{ + CacheKey: 123, + Scope: ScopeTest, + } + + cleanupCalledBack := 0 + env := envMock{onCacheResult: func(opt CacheOptions, f FixtureFunction) interface{} { + opt.additionlSkipExternalCalls-- + require.Equal(t, inOpt, opt) + res, _ := f() + return res.Value + }} + + f := func() (*GenericResult[int], error) { + cleanup := func() { + cleanupCalledBack++ + } + return NewGenericResultWithCleanup(2, cleanup), nil + } + res := CacheResult(env, f, inOpt) + require.Equal(t, 2, res) + }) + t.Run("SkipAdditionalCache", func(t *testing.T) { + test := &internal.TestMock{TestName: t.Name()} + env := newTestEnv(test) + + f1 := func() int { + return CacheResult(env, func() (*GenericResult[int], error) { + return NewGenericResult(1), nil + }) + } + f2 := func() int { + return CacheResult(env, func() (*GenericResult[int], error) { + return NewGenericResult(2), nil + }) + } + + require.Equal(t, 1, f1()) + require.Equal(t, 2, f2()) + }) +} + +func TestCacheResultPanic(t *testing.T) { + t.Run("Simple", func(t *testing.T) { + at := assert.New(t) + tMock := &internal.TestMock{TestName: "mock", SkipGoexit: true} + e := New(tMock) + + rndFix := func(e Env) int { + return CacheResult(e, func() (*GenericResult[int], error) { + return NewGenericResult(rand.Int()), nil + }) + } + first := rndFix(e) + second := rndFix(e) + + at.Equal(first, second) + }) + t.Run("Options", func(t *testing.T) { + at := assert.New(t) + tMock := &internal.TestMock{TestName: "mock", SkipGoexit: true} + e := New(tMock) + + rndFix := func(e Env, name string) int { + return CacheResult(e, func() (*GenericResult[int], error) { + return NewGenericResult(rand.Int()), nil + }, CacheOptions{CacheKey: name}) + } + first1 := rndFix(e, "first") + first2 := rndFix(e, "first") + second1 := rndFix(e, "second") + second2 := rndFix(e, "second") + + at.Equal(first1, first2) + at.Equal(second1, second2) + at.NotEqual(first1, second1) + }) + t.Run("Panic", func(t *testing.T) { + at := assert.New(t) + tMock := &internal.TestMock{TestName: "mock", SkipGoexit: true} + e := New(tMock) + + rndFix := func(e Env, name string) int { + return CacheResult(e, func() (*GenericResult[int], error) { + return NewGenericResult(rand.Int()), nil + }, CacheOptions{CacheKey: name}, CacheOptions{CacheKey: name}) + } + at.Panics(func() { + rndFix(e, "first") + }) + }) } type envMock struct { onCache func(params interface{}, opt *FixtureOptions, f FixtureCallbackFunc) interface{} onCacheWithCleanup func(params interface{}, opt *FixtureOptions, f FixtureCallbackWithCleanupFunc) interface{} + onCacheResult func(opts CacheOptions, f FixtureFunction) interface{} } func (e envMock) T() T { @@ -108,3 +207,16 @@ func (e envMock) Cache(params interface{}, opt *FixtureOptions, f FixtureCallbac func (e envMock) CacheWithCleanup(params interface{}, opt *FixtureOptions, f FixtureCallbackWithCleanupFunc) interface{} { return e.onCacheWithCleanup(params, opt, f) } + +func (e envMock) CacheResult(f FixtureFunction, options ...CacheOptions) interface{} { + var opts CacheOptions + switch len(options) { + case 0: + // pass + case 1: + opts = options[0] + default: + panic(fmt.Errorf("max options len is 1, given: %v", len(options))) + } + return e.onCacheResult(opts, f) +} diff --git a/env_test.go b/env_test.go index 6457fc3..ad28420 100644 --- a/env_test.go +++ b/env_test.go @@ -3,6 +3,7 @@ package fixenv import ( "errors" "github.com/rekby/fixenv/internal" + "math/rand" "runtime" "sync" "testing" @@ -301,6 +302,86 @@ func Test_Env_CacheWithCleanup(t *testing.T) { }) } +func Test_Env_CacheResult(t *testing.T) { + t.Run("Simple", func(t *testing.T) { + at := assert.New(t) + tMock := &internal.TestMock{TestName: "mock", SkipGoexit: true} + e := New(tMock) + + rndFix := func(e Env) int { + return e.CacheResult(func() (*Result, error) { + return NewResult(rand.Int()), nil + }).(int) + } + first := rndFix(e) + second := rndFix(e) + + at.Equal(first, second) + }) + t.Run("Options", func(t *testing.T) { + at := assert.New(t) + tMock := &internal.TestMock{TestName: "mock", SkipGoexit: true} + e := New(tMock) + + rndFix := func(e Env, name string) int { + return e.CacheResult(func() (*Result, error) { + return NewResult(rand.Int()), nil + }, CacheOptions{CacheKey: name}).(int) + } + first1 := rndFix(e, "first") + first2 := rndFix(e, "first") + second1 := rndFix(e, "second") + second2 := rndFix(e, "second") + + at.Equal(first1, first2) + at.Equal(second1, second2) + at.NotEqual(first1, second1) + }) + t.Run("WithCleanup", func(t *testing.T) { + tMock := &internal.TestMock{TestName: t.Name()} + env := newTestEnv(tMock) + + callbackCalled := 0 + cleanupCalled := 0 + var callbackFunc FixtureFunction = func() (*Result, error) { + callbackCalled++ + cleanup := func() { + cleanupCalled++ + } + return NewResultWithCleanup(callbackCalled, cleanup), nil + } + + res := env.CacheResult(callbackFunc) + require.Equal(t, 1, res) + require.Equal(t, 1, callbackCalled) + require.Equal(t, cleanupCalled, 0) + + // got value from cache + res = env.CacheResult(callbackFunc) + require.Equal(t, 1, res) + require.Equal(t, 1, callbackCalled) + require.Equal(t, cleanupCalled, 0) + + tMock.CallCleanup() + require.Equal(t, 1, callbackCalled) + require.Equal(t, 1, cleanupCalled) + }) + t.Run("Panic", func(t *testing.T) { + at := assert.New(t) + tMock := &internal.TestMock{TestName: "mock", SkipGoexit: true} + e := New(tMock) + + rndFix := func(e Env, name string) int { + return e.CacheResult(func() (*Result, error) { + return NewResult(rand.Int()), nil + }, CacheOptions{CacheKey: name}, CacheOptions{CacheKey: name}).(int) + } + at.Panics(func() { + rndFix(e, "first") + }) + }) +} + func Test_FixtureWrapper(t *testing.T) { t.Run("ok", func(t *testing.T) { at := assert.New(t) @@ -651,7 +732,6 @@ func TestNewEnv(t *testing.T) { tm.SkipGoexit = true New(tm) - //goland:noinspection GoDeprecation NewEnv(tm) if len(tm.Fatals) == 0 { t.Fatal("bad double login between new and NewEnv") diff --git a/examples/custom_env/custom_env_test.go b/examples/custom_env/custom_env_test.go index bd81a27..98f9798 100644 --- a/examples/custom_env/custom_env_test.go +++ b/examples/custom_env/custom_env_test.go @@ -37,19 +37,21 @@ func NewEnv(t *testing.T) (context.Context, *Env) { } func testServer(e fixenv.Env, response string) *httptest.Server { - return fixenv.CacheWithCleanup(e, response, nil, func() (_ *httptest.Server, cleanup fixenv.FixtureCleanupFunc, err error) { + f := func() (*fixenv.GenericResult[*httptest.Server], error) { resp := []byte(response) server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { _, _ = writer.Write(resp) })) e.T().(testing.TB).Logf("Http server start. %q url: %q", response, server.URL) - cleanup = func() { + cleanup := func() { server.Close() e.T().(testing.TB).Logf("Http server stop. %q url: %q", response, server.URL) } - return server, cleanup, nil - }) + return fixenv.NewGenericResultWithCleanup(server, cleanup), nil + } + + return fixenv.CacheResult(e, f, fixenv.CacheOptions{CacheKey: response}) } func TestHttpServerSelfEnv(t *testing.T) { diff --git a/examples/custom_env_with_shared_content/custom_env_with_shared_content_test.go b/examples/custom_env_with_shared_content/custom_env_with_shared_content_test.go index afe92de..38d9195 100644 --- a/examples/custom_env_with_shared_content/custom_env_with_shared_content_test.go +++ b/examples/custom_env_with_shared_content/custom_env_with_shared_content_test.go @@ -27,17 +27,19 @@ func NewEnv(t *testing.T) *Env { } func testServer(e *Env) *httptest.Server { - return fixenv.CacheWithCleanup(e, "", nil, func() (res *httptest.Server, cleanup fixenv.FixtureCleanupFunc, err error) { + f := func() (*fixenv.GenericResult[*httptest.Server], error) { server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { _, _ = writer.Write([]byte(e.Resp)) })) e.T().(testing.TB).Logf("Http server start, url: %q", server.URL) - cleanup = func() { + cleanup := func() { server.Close() e.T().(testing.TB).Logf("Http server stop, url: %q", server.URL) } - return server, cleanup, nil - }) + return fixenv.NewGenericResultWithCleanup(server, cleanup), nil + } + + return fixenv.CacheResult(e, f) } func TestHttpServer(t *testing.T) { diff --git a/examples/simple/http_server_test.go b/examples/simple/http_server_test.go index 4c78333..fd7e37a 100644 --- a/examples/simple/http_server_test.go +++ b/examples/simple/http_server_test.go @@ -15,19 +15,21 @@ import ( ) func testServer(e fixenv.Env, response string) *httptest.Server { - return e.CacheWithCleanup(response, nil, func() (res interface{}, cleanup fixenv.FixtureCleanupFunc, err error) { + f := func() (*fixenv.Result, error) { resp := []byte(response) server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { _, _ = writer.Write(resp) })) e.T().(testing.TB).Logf("Http server start. %q url: %q", response, server.URL) - cleanup = func() { + cleanup := func() { server.Close() e.T().(testing.TB).Logf("Http server stop. %q url: %q", response, server.URL) } - return server, cleanup, nil - }).(*httptest.Server) + return fixenv.NewResultWithCleanup(server, cleanup), nil + } + + return e.CacheResult(f, fixenv.CacheOptions{CacheKey: response}).(*httptest.Server) } func TestHttpServer(t *testing.T) { diff --git a/examples/simple/package_scope_test.go b/examples/simple/package_scope_test.go index e5b8440..42241ec 100644 --- a/examples/simple/package_scope_test.go +++ b/examples/simple/package_scope_test.go @@ -29,10 +29,11 @@ func TestMain(m *testing.M) { // packageCounter fixture will call without cache once only func packageCounter(e fixenv.Env) int { - return fixenv.Cache(e, "", &fixenv.FixtureOptions{Scope: fixenv.ScopePackage}, func() (res int, err error) { + f := func() (*fixenv.GenericResult[int], error) { packageCounterVal++ - return packageCounterVal, nil - }) + return fixenv.NewGenericResult(packageCounterVal), nil + } + return fixenv.CacheResult(e, f, fixenv.CacheOptions{Scope: fixenv.ScopePackage}) } func TestPackageFirst(t *testing.T) { diff --git a/examples/simple/simple_old_style_test.go b/examples/simple/simple_old_style_test.go index 80d4ae3..9a4a62c 100644 --- a/examples/simple/simple_old_style_test.go +++ b/examples/simple/simple_old_style_test.go @@ -14,10 +14,11 @@ var ( // counterOldStyle fixture - increment globalCounter every non cached call // and return new globalCounter value func counterOldStyle(e fixenv.Env) int { - return e.Cache(nil, nil, func() (res interface{}, err error) { + f := func() (*fixenv.Result, error) { globalCounter++ - return globalCounter, nil - }).(int) + return fixenv.NewResult(globalCounter), nil + } + return e.CacheResult(f).(int) } func TestCounterOldStyle(t *testing.T) { diff --git a/examples/simple/simple_test.go b/examples/simple/simple_test.go index bb67d13..4a42d00 100644 --- a/examples/simple/simple_test.go +++ b/examples/simple/simple_test.go @@ -12,11 +12,13 @@ import ( // counter fixture - increment globalCounter every non cached call // and return new globalCounter value func counter(e fixenv.Env) int { - return fixenv.Cache(e, nil, nil, func() (res int, err error) { + f := func() (*fixenv.GenericResult[int], error) { globalCounter++ e.T().Logf("increment globalCounter to: ") - return globalCounter, nil - }) + return fixenv.NewGenericResult(globalCounter), nil + } + + return fixenv.CacheResult(e, f) } func TestCounter(t *testing.T) { diff --git a/examples/simple/use_cache_params_test.go b/examples/simple/use_cache_params_test.go index e390969..fb8fc45 100644 --- a/examples/simple/use_cache_params_test.go +++ b/examples/simple/use_cache_params_test.go @@ -14,9 +14,11 @@ import ( // namedRandom return random number for new name args // but return same value for all calls with same names func namedRandom(e fixenv.Env, name string) int { - return fixenv.Cache(e, name, nil, func() (res int, err error) { - return rand.Int(), nil - }) + f := func() (*fixenv.GenericResult[int], error) { + return fixenv.NewGenericResult(rand.Int()), nil + } + + return fixenv.CacheResult(e, f, fixenv.CacheOptions{CacheKey: name}) } func TestNamedRandom(t *testing.T) { diff --git a/go.sum b/go.sum index acb88a4..26500d5 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,7 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= diff --git a/interface.go b/interface.go index 38f9b4d..8e730c5 100644 --- a/interface.go +++ b/interface.go @@ -3,18 +3,32 @@ package fixenv import "errors" // Env - fixture cache engine. +// Env interface described TEnv method and need for easy reuse different Envs with +// same fixtures. +// +// The interface can be extended. +// Create own Envs with embed TEnv or the interface for auto-implement all methods +// in the future. type Env interface { // T - return t object of current test/benchmark. T() T - // Cache cache result of f calls + // CacheResult add result of call f to cache and return same result for all + // calls for the same function and cache options within cache scope + CacheResult(f FixtureFunction, options ...CacheOptions) interface{} + + // Cache result of f calls // f call exactly once for every combination of scope and params // params must be json serializable (deserialize not need) + // Deprecated: will be removed in next versions + // Use Env.CacheResult instead. Cache(cacheKey interface{}, opt *FixtureOptions, f FixtureCallbackFunc) interface{} // CacheWithCleanup cache result of f calls // f call exactly once for every combination of scope and params // params must be json serializable (deserialize not need) + // Deprecated: will be removed in next versions + // Use Env.CacheResult instead. CacheWithCleanup(cacheKey interface{}, opt *FixtureOptions, f FixtureCallbackWithCleanupFunc) interface{} } @@ -79,6 +93,37 @@ type FixtureOptions struct { cleanupFunc FixtureCleanupFunc } +// FixtureFunction - callback function with structured result +type FixtureFunction func() (*Result, error) + +// Result of fixture callback +type Result struct { + Value interface{} + ResultAdditional +} + +type ResultAdditional struct { + Cleanup FixtureCleanupFunc +} + +func NewResult(res interface{}) *Result { + return &Result{Value: res} +} + +func NewResultWithCleanup(res interface{}, cleanup FixtureCleanupFunc) *Result { + return &Result{Value: res, ResultAdditional: ResultAdditional{Cleanup: cleanup}} +} + +type CacheOptions struct { + // Scope for cache result + Scope CacheScope + + // Key for cache results, must be json serializable value + CacheKey interface{} + + additionlSkipExternalCalls int +} + // T is subtype of testing.TB type T interface { // Cleanup registers a function to be called when the test (or subtest) and all its subtests complete. diff --git a/result_generic.go b/result_generic.go new file mode 100644 index 0000000..9172458 --- /dev/null +++ b/result_generic.go @@ -0,0 +1,4 @@ +//go:build go1.18 +// +build go1.18 + +package fixenv diff --git a/sf/context.go b/sf/context.go index d8ae8e9..d5efbdd 100644 --- a/sf/context.go +++ b/sf/context.go @@ -6,8 +6,9 @@ import ( ) func Context(e fixenv.Env) context.Context { - return e.CacheWithCleanup(nil, nil, func() (res interface{}, _ fixenv.FixtureCleanupFunc, _ error) { + f := func() (*fixenv.Result, error) { ctx, ctxCancel := context.WithCancel(context.Background()) - return ctx, fixenv.FixtureCleanupFunc(ctxCancel), nil - }).(context.Context) + return fixenv.NewResultWithCleanup(ctx, fixenv.FixtureCleanupFunc(ctxCancel)), nil + } + return e.CacheResult(f).(context.Context) } diff --git a/sf/filesystem.go b/sf/filesystem.go index f844afd..5f0b3ed 100644 --- a/sf/filesystem.go +++ b/sf/filesystem.go @@ -8,7 +8,7 @@ import ( // TempDir return path for existet temporary folder // the folder will remove after test finish with all contents func TempDir(e fixenv.Env) string { - return e.CacheWithCleanup(nil, nil, func() (res interface{}, cleanup fixenv.FixtureCleanupFunc, err error) { + f := func() (*fixenv.Result, error) { dir, err := os.MkdirTemp("", "fixenv-auto-") mustNoErr(e, err, "failed to create temp dir: %v", err) e.T().Logf("Temp dir created: %v", dir) @@ -16,8 +16,9 @@ func TempDir(e fixenv.Env) string { _ = os.RemoveAll(dir) e.T().Logf("Temp dir removed: %v", dir) } - return dir, clean, nil - }).(string) + return fixenv.NewResultWithCleanup(dir, clean), nil + } + return e.CacheResult(f).(string) } // TempFile return path to empty existed file in TempDir @@ -28,11 +29,15 @@ func TempFile(e fixenv.Env) string { // TempFileNamed return path to empty file in TempDir // pattern is pattern for os.CreateTemp func TempFileNamed(e fixenv.Env, pattern string) string { - return e.Cache(nil, nil, func() (res interface{}, err error) { + f := func() (*fixenv.Result, error) { dir := TempDir(e) f, err := os.CreateTemp(dir, pattern) mustNoErr(e, err, "failed to create temp file: %w", err) fName := f.Name() - return fName, f.Close() - }).(string) + err = f.Close() + mustNoErr(e, err, "failed to close temp file during initialize: %w", err) + return fixenv.NewResult(fName), nil + } + + return e.CacheResult(f).(string) } diff --git a/sf/http.go b/sf/http.go index 301b41f..71a5fe7 100644 --- a/sf/http.go +++ b/sf/http.go @@ -6,8 +6,9 @@ import ( ) func HTTPServer(e fixenv.Env) *httptest.Server { - return e.CacheWithCleanup(nil, nil, func() (res interface{}, cleanup fixenv.FixtureCleanupFunc, err error) { + f := func() (*fixenv.Result, error) { server := httptest.NewServer(nil) - return server, server.Close, nil - }).(*httptest.Server) + return fixenv.NewResultWithCleanup(server, server.Close), nil + } + return e.CacheResult(f).(*httptest.Server) } diff --git a/sf/network.go b/sf/network.go index d2cd4c4..919d310 100644 --- a/sf/network.go +++ b/sf/network.go @@ -10,13 +10,14 @@ func FreeLocalTCPAddress(e fixenv.Env) string { } func FreeLocalTCPAddressNamed(e fixenv.Env, name string) string { - return e.Cache(name, nil, func() (res interface{}, err error) { + f := func() (*fixenv.Result, error) { listener := LocalTCPListenerNamed(e, "FreeLocalTCPAddressNamed-"+name) addr := listener.Addr().String() - err = listener.Close() + err := listener.Close() mustNoErr(e, err, "failed to close temp listener: %v", err) - return addr, nil - }).(string) + return fixenv.NewResult(addr), nil + } + return e.CacheResult(f, fixenv.CacheOptions{CacheKey: name}).(string) } func LocalTCPListener(e fixenv.Env) *net.TCPListener {