Skip to content

Commit

Permalink
make testcase.Var#ID a dedicated type
Browse files Browse the repository at this point in the history
Many code editors’ intellisense often prioritised suggesting Var[string]#ID for string values instead of Var[string]#Get,
which can easily lead to headaches when auto-completion accidentally selects the wrong option.
  • Loading branch information
adamluzsi committed Nov 7, 2024
1 parent 54654bc commit ce7e5fa
Show file tree
Hide file tree
Showing 10 changed files with 72 additions and 70 deletions.
4 changes: 2 additions & 2 deletions Spec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ func TestSpec_Context(t *testing.T) {
var allSideEffect [][]string
var sideEffect []string

v := testcase.Var[int]{ID: strconv.Itoa(rand.Int())}
v := testcase.Var[int]{ID: testcase.VarID(strconv.Itoa(rand.Int()))}
nest1Value := rand.Int()
nest2Value := rand.Int()
nest3Value := rand.Int()
Expand Down Expand Up @@ -226,7 +226,7 @@ func TestSpec_ParallelSafeVariableSupport(t *testing.T) {
s := testcase.NewSpec(t)
s.Parallel()

v := testcase.Var[int]{ID: strconv.Itoa(rand.Int())}
v := testcase.Var[int]{ID: testcase.VarID(strconv.Itoa(rand.Int()))}
nest1Value := rand.Int()
nest2Value := rand.Int()
nest3Value := rand.Int()
Expand Down
2 changes: 1 addition & 1 deletion T.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ func (t *T) contexts() []*Spec {
return t.cache.contexts
}

func (t *T) hasOnLetHookApplied(name string) bool {
func (t *T) hasOnLetHookApplied(name VarID) bool {
for _, c := range t.contexts() {
if ok := c.vars.hasOnLetHookApplied(name); ok {
return ok
Expand Down
8 changes: 5 additions & 3 deletions Var.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
type Var[V any] struct {
// ID is the testCase spec variable group from where the cached value can be accessed later on.
// ID is Mandatory when you create a variable, else the empty string will be used as the variable group.
ID string
ID VarID
// Init is an optional constructor definition that will be used when Var is bonded to a *Spec without constructor function passed to the Let function.
// The goal of this field to initialize a variable that can be reused across different testing suites by bounding the Var to a given testing suite.
//
Expand All @@ -47,17 +47,19 @@ type Var[V any] struct {
Deps Vars
}

type VarID string

type Vars []tetcaseVar

type tetcaseVar interface {
isTestcaseVar()
id() string
id() VarID
get(t *T) any
bind(s *Spec)
}

func (Var[V]) isTestcaseVar() {}
func (v Var[V]) id() string { return v.ID }
func (v Var[V]) id() VarID { return v.ID }

type VarInit[V any] func(*T) V

Expand Down
6 changes: 3 additions & 3 deletions Var_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ func TestVar(t *testing.T) {
// This should not be the case for anything else outside the testing framework.
s.HasSideEffect()
rnd := random.New(random.CryptoSeed{})
var testVar = testcase.Var[int]{ID: rnd.StringNWithCharset(5, "abcdefghijklmnopqrstuvwxyz")}
var testVar = testcase.Var[int]{ID: testcase.VarID(rnd.StringNWithCharset(5, "abcdefghijklmnopqrstuvwxyz"))}
expected := rnd.Int()

stub := &doubles.TB{}
willFatal := willFatalWithMessageFn(stub)
willFatalWithVariableNotFoundMessage := func(s *testcase.Spec, tb testing.TB, varName string, blk func(*testcase.T)) {
willFatalWithVariableNotFoundMessage := func(s *testcase.Spec, tb testing.TB, varName testcase.VarID, blk func(*testcase.T)) {
tct := testcase.NewTWithSpec(stub, s)
assert.Must(tb).Contain(willFatal(t, func() { blk(tct) }),
fmt.Sprintf("Variable %q is not found.", varName))
Expand Down Expand Up @@ -207,7 +207,7 @@ func TestVar(t *testing.T) {
})
})

willFatalWithOnLetMissing := func(s *testcase.Spec, tb testing.TB, varName string, blk func(*testcase.T)) {
willFatalWithOnLetMissing := func(s *testcase.Spec, tb testing.TB, varName testcase.VarID, blk func(*testcase.T)) {
tct := testcase.NewTWithSpec(stub, s)
assert.Must(tb).Contain(willFatal(t, func() { blk(tct) }),
fmt.Sprintf("%s Var has Var.OnLet. You must use Var.Let, Var.LetValue to initialize it properly.", varName))
Expand Down
4 changes: 2 additions & 2 deletions backward.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import "go.llib.dev/testcase/assert"
// thus Let has moved to be a pkg-level function in the package.
//
// DEPRECATED: use testcase.Let instead testcase#Spec.Let.
func (spec *Spec) Let(varName string, blk VarInit[any]) Var[any] {
func (spec *Spec) Let(varName VarID, blk VarInit[any]) Var[any] {
return let[any](spec, varName, blk)
}

Expand All @@ -16,7 +16,7 @@ func (spec *Spec) Let(varName string, blk VarInit[any]) Var[any] {
// thus LetValue has moved to be a pkg-level function in the package.
//
// DEPRECATED: use testcase.LetValue instead testcase#Spec.LetValue.
func (spec *Spec) LetValue(varName string, value any) Var[any] {
func (spec *Spec) LetValue(varName VarID, value any) Var[any] {
return letValue[any](spec, varName, value)
}

Expand Down
4 changes: 2 additions & 2 deletions docs/contracts.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ type RoleInterfaceContract struct {

type RoleInterfaceContractSubject interface {
mypkg.RoleInterfaceName
FindByID(ctx context.Context, id string) (mypkg.XY, bool, error)
DeleteByID(ctx context.Context, id string) error
FindByID(ctx context.Context, id VarID) (mypkg.XY, bool, error)
DeleteByID(ctx context.Context, id VarID) error
}
```

Expand Down
18 changes: 9 additions & 9 deletions let.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ func LetValue[V any](spec *Spec, value V) Var[V] {
return letValue[V](spec, makeVarID(spec), value)
}

func let[V any](spec *Spec, varID string, blk VarInit[V]) Var[V] {
func let[V any](spec *Spec, varID VarID, blk VarInit[V]) Var[V] {
helper(spec.testingTB).Helper()
if spec.immutable {
spec.testingTB.Fatalf(warnEventOnImmutableFormat, `Let`)
Expand All @@ -108,7 +108,7 @@ func let[V any](spec *Spec, varID string, blk VarInit[V]) Var[V] {
return Var[V]{ID: varID, Init: blk}
}

func letValue[V any](spec *Spec, varName string, value V) Var[V] {
func letValue[V any](spec *Spec, varName VarID, value V) Var[V] {
helper(spec.testingTB).Helper()
if reflects.IsMutable(value) {
spec.testingTB.Fatalf(panicMessageForLetValue, value)
Expand All @@ -121,7 +121,7 @@ func letValue[V any](spec *Spec, varName string, value V) Var[V] {
}

// latest decl is the first and the deeper you want to reach back, the higher the index
func findCurrentDeclsFor(spec *Spec, varName string) []variablesInitBlock {
func findCurrentDeclsFor(spec *Spec, varName VarID) []variablesInitBlock {
var decls []variablesInitBlock
for _, s := range spec.specsFromCurrent() {
if decl, ok := s.vars.defs[varName]; ok {
Expand All @@ -131,13 +131,13 @@ func findCurrentDeclsFor(spec *Spec, varName string) []variablesInitBlock {
return decls
}

func makeVarID(spec *Spec) string {
func makeVarID(spec *Spec) VarID {
helper(spec.testingTB).Helper()
location := caller.GetLocation(false)
// when variable is declared within a loop
// providing a variable ID offset is required to identify the variable uniquely.

varIDIndex := make(map[string]struct{})
varIDIndex := make(map[VarID]struct{})
for _, s := range spec.specsFromParent() {
for k := range s.vars.locks {
varIDIndex[k] = struct{}{}
Expand All @@ -154,19 +154,19 @@ func makeVarID(spec *Spec) string {
}

var (
id string
id VarID
offset int
)
positioning:
for {
// quick path for the majority of the case.
if _, ok := varIDIndex[location]; !ok {
id = location
if _, ok := varIDIndex[VarID(location)]; !ok {
id = VarID(location)
break positioning
}

offset++
id = fmt.Sprintf("%s#[%d]", location, offset)
id = VarID(fmt.Sprintf("%s#[%d]", location, offset))
if _, ok := varIDIndex[id]; !ok {
break positioning
}
Expand Down
2 changes: 1 addition & 1 deletion let/let.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func As[To, From any](Var testcase.Var[From]) testcase.Var[To] {
panic(fmt.Sprintf("you can't have %s as %s", fromType.String(), toType.String()))
}
return testcase.Var[To]{
ID: fmt.Sprintf("%s AS %T #%d", Var.ID, *new(To), asID),
ID: testcase.VarID(fmt.Sprintf("%s AS %T #%d", Var.ID, *new(To), asID)),
Init: func(t *testcase.T) To {
var rFrom = reflect.ValueOf(Var.Get(t))
return rFrom.Convert(toType).Interface().(To)
Expand Down
2 changes: 1 addition & 1 deletion let_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ func TestLet_letVarIDInNonCoreTestcasePackage(t *testing.T) {

s := testcase.NewSpec(t)
resp := httpspec.LetResponseRecorder(s)
t.Logf(resp.ID)
t.Logf("id: %s", resp.ID)
assert.NotContain(t, resp.ID, "_test.go")
assert.NotContain(t, resp.ID, filepath.Base(frame.File))
assert.Contain(t, resp.ID, filepath.Dir(frame.File))
Expand Down
Loading

0 comments on commit ce7e5fa

Please sign in to comment.