diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 19e92390..6d7b2cb6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,7 +10,7 @@ jobs: test: strategy: matrix: - go-version: [1.9.x, 1.10.x, 1.11.x, 1.12.x, 1.13.x, 1.14.x, 1.15.x, 1.16.x, 1.17.x, 1.18.x, tip] + go-version: [1.16.x, 1.17.x, 1.18.x, tip] full-tests: [false] include: - go-version: 1.19.x @@ -41,7 +41,7 @@ jobs: if: matrix.full-tests run: | curl -sL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | - sh -s -- -b $HOME/go/bin v1.49.0 + sh -s -- -b $HOME/go/bin v1.50.0 echo $PATH $HOME/go/bin/golangci-lint run --max-issues-per-linter 0 \ --max-same-issues 0 \ @@ -71,16 +71,6 @@ jobs: GO_TEST_RACE_SAFE_FLAGS="$cover_flags-race-safe.out" fi - case ${{ matrix.go-version }} in - 1.9.x | 1.10.x) # Before go 1.11, go modules are not available - mkdir -p ../src/github.com/maxatome - ln -s $(pwd) ../src/github.com/$GITHUB_REPOSITORY - export GOPATH=$(dirname $(pwd)) - cd $GOPATH/src/github.com/$GITHUB_REPOSITORY - go get -t ./... - ;; - esac - export GORACE="halt_on_error=1" echo "CLASSIC ===========================================" go test $GO_TEST_FLAGS ./... diff --git a/README.md b/README.md index 6206697d..385c0fe1 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ go-testdeep **Extremely flexible golang deep comparison, extends the go testing package.** -Currently supports go 1.9 → 1.19. +Currently supports go 1.16 → 1.19. - [Latest news](#latest-news) - [Synopsis](#synopsis) diff --git a/helpers/tdsuite/suite.go b/helpers/tdsuite/suite.go index d777f79c..da6f219a 100644 --- a/helpers/tdsuite/suite.go +++ b/helpers/tdsuite/suite.go @@ -24,10 +24,9 @@ var tType = reflect.TypeOf((*td.T)(nil)) // tests suite, Setup method is called once before any test runs. If // Setup returns an error, the tests suite aborts: no tests are run. // -// Starting go1.14, t.Cleanup() can be called in Setup method. It can -// replace the definition of a [Destroy] method. It can also be used -// together, in this case cleanup registered functions are called -// after [Destroy]. +// t.Cleanup() can be called in Setup method. It can replace the +// definition of a [Destroy] method. It can also be used together, in +// this case cleanup registered functions are called after [Destroy]. type Setup interface { Setup(t *td.T) error } @@ -37,10 +36,9 @@ type Setup interface { // itself. If PreTest returns an error, the subtest aborts: the test // is not run. // -// Starting go1.14, t.Cleanup() can be called in PreTest method. It can -// replace the definition of a [PostTest] method. It can also be used -// together, in this case cleanup registered functions are called -// after [PostTest]. +// t.Cleanup() can be called in PreTest method. It can replace the +// definition of a [PostTest] method. It can also be used together, in +// this case cleanup registered functions are called after [PostTest]. type PreTest interface { PreTest(t *td.T, testName string) error } diff --git a/helpers/tdsuite/suite_go114_test.go b/helpers/tdsuite/suite_go114_test.go deleted file mode 100644 index a04fc715..00000000 --- a/helpers/tdsuite/suite_go114_test.go +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (c) 2021, Maxime Soulé -// All rights reserved. -// -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. - -//go:build go1.14 -// +build go1.14 - -package tdsuite_test - -import ( - "testing" - - "github.com/maxatome/go-testdeep/helpers/tdsuite" - "github.com/maxatome/go-testdeep/td" -) - -// FullCleanup has tests and all possible hooks. -type FullCleanup struct{ base } - -func (f *FullCleanup) Setup(t *td.T) error { f.rec(); return nil } -func (f *FullCleanup) PreTest(t *td.T, tn string) error { - f.rec(tn) - t.Cleanup(func() { f.rec(tn) }) - return nil -} - -func (f *FullCleanup) PostTest(t *td.T, tn string) error { - f.rec(tn) - t.Cleanup(func() { f.rec(tn) }) - return nil -} - -func (f *FullCleanup) BetweenTests(t *td.T, prev, next string) error { - f.rec(prev, next) - return nil -} -func (f *FullCleanup) Destroy(t *td.T) error { f.rec(); return nil } - -func (f *FullCleanup) Test1(t *td.T) { - f.rec() - t.Cleanup(func() { f.rec() }) -} - -func (f *FullCleanup) Test2(assert *td.T, require *td.T) { - f.rec() - assert.Cleanup(func() { f.rec() }) -} - -func (f *FullCleanup) Test3(t *td.T) { - f.rec() - t.Cleanup(func() { f.rec() }) -} -func (f *FullCleanup) Testimony(t *td.T) {} // not a test method - -var ( - _ tdsuite.Setup = (*FullCleanup)(nil) - _ tdsuite.PreTest = (*FullCleanup)(nil) - _ tdsuite.PostTest = (*FullCleanup)(nil) - _ tdsuite.BetweenTests = (*FullCleanup)(nil) - _ tdsuite.Destroy = (*FullCleanup)(nil) -) - -func TestRunCleanup(t *testing.T) { - t.Run("Full", func(t *testing.T) { - suite := FullCleanup{} - td.CmpTrue(t, tdsuite.Run(t, &suite)) - ok := td.Cmp(t, suite.calls, []string{ - "Setup", - /**/ "PreTest+Test1", - /**/ "Test1", - /**/ "PostTest+Test1", - /**/ "PostTest.Cleanup+Test1", - /**/ "Test1.Cleanup", - /**/ "PreTest.Cleanup+Test1", - "BetweenTests+Test1+Test2", - /**/ "PreTest+Test2", - /**/ "Test2", - /**/ "PostTest+Test2", - /**/ "PostTest.Cleanup+Test2", - /**/ "Test2.Cleanup", - /**/ "PreTest.Cleanup+Test2", - "BetweenTests+Test2+Test3", - /**/ "PreTest+Test3", - /**/ "Test3", - /**/ "PostTest+Test3", - /**/ "PostTest.Cleanup+Test3", - /**/ "Test3.Cleanup", - /**/ "PreTest.Cleanup+Test3", - "Destroy", - }) - if !ok { - for _, c := range suite.calls { - switch c[0] { - case 'S', 'B', 'D': - t.Log(c) - default: - t.Log(" ", c) - } - } - } - }) -} diff --git a/helpers/tdsuite/suite_test.go b/helpers/tdsuite/suite_test.go index 93f37c86..df35c885 100644 --- a/helpers/tdsuite/suite_test.go +++ b/helpers/tdsuite/suite_test.go @@ -737,3 +737,90 @@ func TestRunErrors(t *testing.T) { }) }) } + +// FullCleanup has tests and all possible hooks. +type FullCleanup struct{ base } + +func (f *FullCleanup) Setup(t *td.T) error { f.rec(); return nil } +func (f *FullCleanup) PreTest(t *td.T, tn string) error { + f.rec(tn) + t.Cleanup(func() { f.rec(tn) }) + return nil +} + +func (f *FullCleanup) PostTest(t *td.T, tn string) error { + f.rec(tn) + t.Cleanup(func() { f.rec(tn) }) + return nil +} + +func (f *FullCleanup) BetweenTests(t *td.T, prev, next string) error { + f.rec(prev, next) + return nil +} +func (f *FullCleanup) Destroy(t *td.T) error { f.rec(); return nil } + +func (f *FullCleanup) Test1(t *td.T) { + f.rec() + t.Cleanup(func() { f.rec() }) +} + +func (f *FullCleanup) Test2(assert *td.T, require *td.T) { + f.rec() + assert.Cleanup(func() { f.rec() }) +} + +func (f *FullCleanup) Test3(t *td.T) { + f.rec() + t.Cleanup(func() { f.rec() }) +} +func (f *FullCleanup) Testimony(t *td.T) {} // not a test method + +var ( + _ tdsuite.Setup = (*FullCleanup)(nil) + _ tdsuite.PreTest = (*FullCleanup)(nil) + _ tdsuite.PostTest = (*FullCleanup)(nil) + _ tdsuite.BetweenTests = (*FullCleanup)(nil) + _ tdsuite.Destroy = (*FullCleanup)(nil) +) + +func TestRunCleanup(t *testing.T) { + t.Run("Full", func(t *testing.T) { + suite := FullCleanup{} + td.CmpTrue(t, tdsuite.Run(t, &suite)) + ok := td.Cmp(t, suite.calls, []string{ + "Setup", + /**/ "PreTest+Test1", + /**/ "Test1", + /**/ "PostTest+Test1", + /**/ "PostTest.Cleanup+Test1", + /**/ "Test1.Cleanup", + /**/ "PreTest.Cleanup+Test1", + "BetweenTests+Test1+Test2", + /**/ "PreTest+Test2", + /**/ "Test2", + /**/ "PostTest+Test2", + /**/ "PostTest.Cleanup+Test2", + /**/ "Test2.Cleanup", + /**/ "PreTest.Cleanup+Test2", + "BetweenTests+Test2+Test3", + /**/ "PreTest+Test3", + /**/ "Test3", + /**/ "PostTest+Test3", + /**/ "PostTest.Cleanup+Test3", + /**/ "Test3.Cleanup", + /**/ "PreTest.Cleanup+Test3", + "Destroy", + }) + if !ok { + for _, c := range suite.calls { + switch c[0] { + case 'S', 'B', 'D': + t.Log(c) + default: + t.Log(" ", c) + } + } + } + }) +} diff --git a/helpers/tdutil/map.go b/helpers/tdutil/map.go index fda51dfa..4c7778f5 100644 --- a/helpers/tdutil/map.go +++ b/helpers/tdutil/map.go @@ -9,6 +9,8 @@ package tdutil import ( "reflect" "sort" + + "github.com/maxatome/go-testdeep/internal/visited" ) // MapSortedKeys returns a slice of all sorted keys of map m. It @@ -18,3 +20,72 @@ func MapSortedKeys(m reflect.Value) []reflect.Value { sort.Sort(SortableValues(ks)) return ks } + +type kv struct { + key reflect.Value + value reflect.Value +} + +type kvSlice struct { + v visited.Visited + s []kv +} + +func newKvSlice(l int) *kvSlice { + s := kvSlice{} + if l > 0 { + s.s = make([]kv, 0, l) + if l > 1 { + s.v = visited.NewVisited() + } + } + return &s +} + +func (s *kvSlice) Len() int { return len(s.s) } +func (s *kvSlice) Less(i, j int) bool { + return cmp(s.v, s.s[i].key, s.s[j].key) < 0 +} +func (s *kvSlice) Swap(i, j int) { s.s[i], s.s[j] = s.s[j], s.s[i] } + +// MapEach calls fn for each key/value pair of map m. If fn +// returns false, it will not be called again. +func MapEach(m reflect.Value, fn func(k, v reflect.Value) bool) bool { + kvs := newKvSlice(m.Len()) + iter := m.MapRange() + for iter.Next() { + kvs.s = append(kvs.s, kv{key: iter.Key(), value: iter.Value()}) + } + sort.Sort(kvs) + + for _, kv := range kvs.s { + if !fn(kv.key, kv.value) { + return false + } + } + return true +} + +// MapEachValue calls fn for each value of map m. If fn returns +// false, it will not be called again. +func MapEachValue(m reflect.Value, fn func(k reflect.Value) bool) bool { + iter := m.MapRange() + for iter.Next() { + if !fn(iter.Value()) { + return false + } + } + return true +} + +// MapSortedValues returns a slice of all sorted values of map m. It +// panics if m's [reflect.Kind] is not [reflect.Map]. +func MapSortedValues(m reflect.Value) []reflect.Value { + vs := make([]reflect.Value, 0, m.Len()) + iter := m.MapRange() + for iter.Next() { + vs = append(vs, iter.Value()) + } + sort.Sort(SortableValues(vs)) + return vs +} diff --git a/helpers/tdutil/map_go112.go b/helpers/tdutil/map_go112.go deleted file mode 100644 index e89101d8..00000000 --- a/helpers/tdutil/map_go112.go +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) 2018, Maxime Soulé -// All rights reserved. -// -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. - -//go:build go1.12 -// +build go1.12 - -package tdutil - -import ( - "reflect" - "sort" - - "github.com/maxatome/go-testdeep/internal/visited" -) - -type kv struct { - key reflect.Value - value reflect.Value -} - -type kvSlice struct { - v visited.Visited - s []kv -} - -func newKvSlice(l int) *kvSlice { - s := kvSlice{} - if l > 0 { - s.s = make([]kv, 0, l) - if l > 1 { - s.v = visited.NewVisited() - } - } - return &s -} - -func (s *kvSlice) Len() int { return len(s.s) } -func (s *kvSlice) Less(i, j int) bool { - return cmp(s.v, s.s[i].key, s.s[j].key) < 0 -} -func (s *kvSlice) Swap(i, j int) { s.s[i], s.s[j] = s.s[j], s.s[i] } - -// MapEach calls fn for each key/value pair of map m. If fn -// returns false, it will not be called again. -func MapEach(m reflect.Value, fn func(k, v reflect.Value) bool) bool { - kvs := newKvSlice(m.Len()) - iter := m.MapRange() - for iter.Next() { - kvs.s = append(kvs.s, kv{key: iter.Key(), value: iter.Value()}) - } - sort.Sort(kvs) - - for _, kv := range kvs.s { - if !fn(kv.key, kv.value) { - return false - } - } - return true -} - -// MapEachValue calls fn for each value of map m. If fn returns -// false, it will not be called again. -func MapEachValue(m reflect.Value, fn func(k reflect.Value) bool) bool { - iter := m.MapRange() - for iter.Next() { - if !fn(iter.Value()) { - return false - } - } - return true -} - -// MapSortedValues returns a slice of all sorted values of map m. It -// panics if m's [reflect.Kind] is not [reflect.Map]. -func MapSortedValues(m reflect.Value) []reflect.Value { - vs := make([]reflect.Value, 0, m.Len()) - iter := m.MapRange() - for iter.Next() { - vs = append(vs, iter.Value()) - } - sort.Sort(SortableValues(vs)) - return vs -} diff --git a/helpers/tdutil/map_other.go b/helpers/tdutil/map_other.go deleted file mode 100644 index a8459424..00000000 --- a/helpers/tdutil/map_other.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) 2018, Maxime Soulé -// All rights reserved. -// -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. - -//go:build !go1.12 -// +build !go1.12 - -package tdutil - -import ( - "reflect" - "sort" -) - -// MapEach calls fn for each key/value pair of map m. If fn -// returns false, it will not be called again. -func MapEach(m reflect.Value, fn func(k, v reflect.Value) bool) bool { - ks := MapSortedKeys(m) - for _, vkey := range ks { - if !fn(vkey, m.MapIndex(vkey)) { - return false - } - } - return true -} - -// MapEachValue calls fn for each value of map m. If fn returns -// false, it will not be called again. -func MapEachValue(m reflect.Value, fn func(k reflect.Value) bool) bool { - for _, vkey := range m.MapKeys() { - if !fn(m.MapIndex(vkey)) { - return false - } - } - return true -} - -// MapSortedValues returns a slice of all sorted values of map m. It -// panics if m's [reflect.Kind] is not [reflect.Map]. -func MapSortedValues(m reflect.Value) []reflect.Value { - vs := make([]reflect.Value, 0, m.Len()) - for _, vkey := range m.MapKeys() { - vs = append(vs, m.MapIndex(vkey)) - } - sort.Sort(SortableValues(vs)) - return vs -} diff --git a/helpers/tdutil/map_go112_test.go b/helpers/tdutil/map_private_test.go similarity index 97% rename from helpers/tdutil/map_go112_test.go rename to helpers/tdutil/map_private_test.go index c131fbf0..983a4b69 100644 --- a/helpers/tdutil/map_go112_test.go +++ b/helpers/tdutil/map_private_test.go @@ -4,9 +4,6 @@ // This source code is licensed under the BSD-style license found in the // LICENSE file in the root directory of this source tree. -//go:build go1.12 -// +build go1.12 - package tdutil import ( diff --git a/helpers/tdutil/name.go b/helpers/tdutil/name.go index 210eaa8c..7e6fc777 100644 --- a/helpers/tdutil/name.go +++ b/helpers/tdutil/name.go @@ -7,7 +7,6 @@ package tdutil import ( - "bytes" "fmt" "io" "strings" @@ -22,7 +21,7 @@ func BuildTestName(args ...any) string { return "" } - var b bytes.Buffer + var b strings.Builder FbuildTestName(&b, args...) return b.String() } diff --git a/internal/ctxerr/op_error.go b/internal/ctxerr/op_error.go index 31e92884..ea674d64 100644 --- a/internal/ctxerr/op_error.go +++ b/internal/ctxerr/op_error.go @@ -7,9 +7,9 @@ package ctxerr import ( - "bytes" "fmt" "reflect" + "strings" "github.com/maxatome/go-testdeep/internal/types" ) @@ -17,7 +17,7 @@ import ( // OpBadUsage returns a string to notice the user he passed a bad // parameter to an operator constructor. func OpBadUsage(op, usage string, param any, pos int, kind bool) *Error { - var b bytes.Buffer + var b strings.Builder fmt.Fprintf(&b, "usage: %s%s, but received ", op, usage) if param == nil { diff --git a/internal/json/parser_go113_test.go b/internal/json/parser_go113_test.go deleted file mode 100644 index e7000d16..00000000 --- a/internal/json/parser_go113_test.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) 2022, Maxime Soulé -// All rights reserved. -// -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. - -//go:build go1.13 -// +build go1.13 - -package json_test - -import ( - "testing" -) - -func TestJSON_go113(t *testing.T) { - // Extend to golang 1.13 accepted numbers - - // as int64 - checkJSON(t, `4_2`, `42`) - checkJSON(t, `+4_2`, `42`) - checkJSON(t, `-4_2`, `-42`) - - checkJSON(t, `0b101010`, `42`) - checkJSON(t, `-0b101010`, `-42`) - checkJSON(t, `+0b101010`, `42`) - - checkJSON(t, `0b10_1010`, `42`) - checkJSON(t, `-0b_10_1010`, `-42`) - checkJSON(t, `+0b10_10_10`, `42`) - - checkJSON(t, `0B101010`, `42`) - checkJSON(t, `-0B101010`, `-42`) - checkJSON(t, `+0B101010`, `42`) - - checkJSON(t, `0B10_1010`, `42`) - checkJSON(t, `-0B_10_1010`, `-42`) - checkJSON(t, `+0B10_10_10`, `42`) - - checkJSON(t, `0_600`, `384`) - checkJSON(t, `-0_600`, `-384`) - checkJSON(t, `+0_600`, `384`) - - checkJSON(t, `0o600`, `384`) - checkJSON(t, `0o_600`, `384`) - checkJSON(t, `-0o600`, `-384`) - checkJSON(t, `-0o6_00`, `-384`) - checkJSON(t, `+0o600`, `384`) - checkJSON(t, `+0o60_0`, `384`) - - checkJSON(t, `0O600`, `384`) - checkJSON(t, `0O_600`, `384`) - checkJSON(t, `-0O600`, `-384`) - checkJSON(t, `-0O6_00`, `-384`) - checkJSON(t, `+0O600`, `384`) - checkJSON(t, `+0O60_0`, `384`) - - checkJSON(t, `0xBad_Face`, `195951310`) - checkJSON(t, `-0x_Bad_Face`, `-195951310`) - checkJSON(t, `+0xBad_Face`, `195951310`) - - checkJSON(t, `0XBad_Face`, `195951310`) - checkJSON(t, `-0X_Bad_Face`, `-195951310`) - checkJSON(t, `+0XBad_Face`, `195951310`) - - // as float64 - checkJSON(t, `0_600.123`, `600.123`) // float64 can not be an octal number - checkJSON(t, `1_5.`, `15`) - checkJSON(t, `0.15e+0_2`, `15`) - checkJSON(t, `0x1p-2`, `0.25`) - checkJSON(t, `0x2.p10`, `2048`) - checkJSON(t, `0x1.Fp+0`, `1.9375`) - checkJSON(t, `0X.8p-0`, `0.5`) - checkJSON(t, `0X_1FFFP-16`, `0.1249847412109375`) -} diff --git a/internal/json/parser_test.go b/internal/json/parser_test.go index 88901e7c..0f85bd82 100644 --- a/internal/json/parser_test.go +++ b/internal/json/parser_test.go @@ -128,6 +128,64 @@ func TestJSON(t *testing.T) { checkJSON(t, `0600.`, `600`) // float64 can not be an octal number checkJSON(t, `.25`, `0.25`) checkJSON(t, `+123.`, `123`) + + // Extend to golang 1.13 accepted numbers + // as int64 + checkJSON(t, `4_2`, `42`) + checkJSON(t, `+4_2`, `42`) + checkJSON(t, `-4_2`, `-42`) + + checkJSON(t, `0b101010`, `42`) + checkJSON(t, `-0b101010`, `-42`) + checkJSON(t, `+0b101010`, `42`) + + checkJSON(t, `0b10_1010`, `42`) + checkJSON(t, `-0b_10_1010`, `-42`) + checkJSON(t, `+0b10_10_10`, `42`) + + checkJSON(t, `0B101010`, `42`) + checkJSON(t, `-0B101010`, `-42`) + checkJSON(t, `+0B101010`, `42`) + + checkJSON(t, `0B10_1010`, `42`) + checkJSON(t, `-0B_10_1010`, `-42`) + checkJSON(t, `+0B10_10_10`, `42`) + + checkJSON(t, `0_600`, `384`) + checkJSON(t, `-0_600`, `-384`) + checkJSON(t, `+0_600`, `384`) + + checkJSON(t, `0o600`, `384`) + checkJSON(t, `0o_600`, `384`) + checkJSON(t, `-0o600`, `-384`) + checkJSON(t, `-0o6_00`, `-384`) + checkJSON(t, `+0o600`, `384`) + checkJSON(t, `+0o60_0`, `384`) + + checkJSON(t, `0O600`, `384`) + checkJSON(t, `0O_600`, `384`) + checkJSON(t, `-0O600`, `-384`) + checkJSON(t, `-0O6_00`, `-384`) + checkJSON(t, `+0O600`, `384`) + checkJSON(t, `+0O60_0`, `384`) + + checkJSON(t, `0xBad_Face`, `195951310`) + checkJSON(t, `-0x_Bad_Face`, `-195951310`) + checkJSON(t, `+0xBad_Face`, `195951310`) + + checkJSON(t, `0XBad_Face`, `195951310`) + checkJSON(t, `-0X_Bad_Face`, `-195951310`) + checkJSON(t, `+0XBad_Face`, `195951310`) + + // as float64 + checkJSON(t, `0_600.123`, `600.123`) // float64 can not be an octal number + checkJSON(t, `1_5.`, `15`) + checkJSON(t, `0.15e+0_2`, `15`) + checkJSON(t, `0x1p-2`, `0.25`) + checkJSON(t, `0x2.p10`, `2048`) + checkJSON(t, `0x1.Fp+0`, `1.9375`) + checkJSON(t, `0X.8p-0`, `0.5`) + checkJSON(t, `0X_1FFFP-16`, `0.1249847412109375`) }) t.Run("Special string cases", func(t *testing.T) { diff --git a/td/cmp_deeply.go b/td/cmp_deeply.go index b8b35884..9c53bcaf 100644 --- a/td/cmp_deeply.go +++ b/td/cmp_deeply.go @@ -147,7 +147,8 @@ func formatError(t TestingT, isFatal bool, err *ctxerr.Error, args ...any) { } func cmpDeeply(ctx ctxerr.Context, t TestingT, got, expected any, - args ...any) bool { + args ...any, +) bool { err := deepValueEqualFinal(ctx, reflect.ValueOf(got), reflect.ValueOf(expected)) if err == nil { diff --git a/td/t_anchor.go b/td/t_anchor.go index 562b7f4c..d2462bba 100644 --- a/td/t_anchor.go +++ b/td/t_anchor.go @@ -289,7 +289,7 @@ func (t *T) initAnchors() { // Do not record a finalizer if no name (should not happen // except perhaps in tests) if name != "" { - cleanupTB(t.TB, func() { + t.Cleanup(func() { allAnchorsMu.Lock() defer allAnchorsMu.Unlock() delete(allAnchors, name) diff --git a/td/t_anchor_go114.go b/td/t_anchor_go114.go deleted file mode 100644 index 3eb4db91..00000000 --- a/td/t_anchor_go114.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2020, Maxime Soulé -// All rights reserved. -// -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. - -//go:build go1.14 -// +build go1.14 - -package td - -import "testing" - -func cleanupTB(tb testing.TB, finalize func()) { - tb.Cleanup(finalize) -} diff --git a/td/t_anchor_other.go b/td/t_anchor_other.go deleted file mode 100644 index ef83d43f..00000000 --- a/td/t_anchor_other.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) 2020, Maxime Soulé -// All rights reserved. -// -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. - -//go:build !go1.14 -// +build !go1.14 - -package td - -import ( - "runtime" - "testing" -) - -func cleanupTB(tb testing.TB, finalize func()) { - runtime.SetFinalizer(tb, func(_ testing.TB) { finalize() }) -} diff --git a/td/t_struct.go b/td/t_struct.go index b051cffc..bd9271ca 100644 --- a/td/t_struct.go +++ b/td/t_struct.go @@ -7,8 +7,8 @@ package td import ( - "bytes" "reflect" + "strings" "sync" "testing" @@ -783,12 +783,12 @@ func (t *T) RunT(name string, f func(t *T)) bool { } func getTrace(args ...any) string { - var b bytes.Buffer + var b strings.Builder tdutil.FbuildTestName(&b, args...) if b.Len() == 0 { b.WriteString("Stack trace:\n") - } else if !bytes.HasSuffix(b.Bytes(), []byte{'\n'}) { + } else if !strings.HasSuffix(b.String(), "\n") { b.WriteByte('\n') } diff --git a/td/td_map.go b/td/td_map.go index b590d9ac..0d20d165 100644 --- a/td/td_map.go +++ b/td/td_map.go @@ -7,9 +7,9 @@ package td import ( - "bytes" "fmt" "reflect" + "strings" "github.com/maxatome/go-testdeep/helpers/tdutil" "github.com/maxatome/go-testdeep/internal/ctxerr" @@ -403,7 +403,7 @@ func (m *tdMap) String() string { return m.stringError() } - buf := &bytes.Buffer{} + var buf strings.Builder if m.kind != allMap { buf.WriteString(m.GetLocation().Func) @@ -418,7 +418,7 @@ func (m *tdMap) String() string { buf.WriteString("{\n") for _, entryInfo := range m.expectedEntries { - fmt.Fprintf(buf, " %s: %s,\n", //nolint: errcheck + fmt.Fprintf(&buf, " %s: %s,\n", //nolint: errcheck util.ToString(entryInfo.key), util.ToString(entryInfo.expected)) } diff --git a/td/td_smuggle.go b/td/td_smuggle.go index 54b03908..9cbd3c18 100644 --- a/td/td_smuggle.go +++ b/td/td_smuggle.go @@ -31,9 +31,6 @@ type SmuggledGot struct { const smuggled = "" var ( - // strconv.ParseComplex only exists from go1.15. - parseComplex func(string, int) (complex128, error) - smuggleFnsMu sync.Mutex smuggleFns = map[any]reflect.Value{} @@ -76,7 +73,7 @@ type smuggleField struct { } func joinFieldsPath(path []smuggleField) string { - var buf bytes.Buffer + var buf strings.Builder for i, part := range path { if part.Indexed { fmt.Fprintf(&buf, "[%s]", part.Name) @@ -220,17 +217,13 @@ func buildFieldsPathFn(path string) (func(any) (smuggleValue, error), error) { } vkey = reflect.ValueOf(f).Convert(tkey) case reflect.Complex64, reflect.Complex128: - if parseComplex != nil { - c, err := parseComplex(field.Name, 128) - if err != nil { - return smuggleValue{}, fmt.Errorf( - "field %q, %q is not a complex number and so cannot match %s map key type", - joinFieldsPath(parts[:idxPart+1]), field.Name, tkey) - } - vkey = reflect.ValueOf(c).Convert(tkey) - break + c, err := strconv.ParseComplex(field.Name, 128) + if err != nil { + return smuggleValue{}, fmt.Errorf( + "field %q, %q is not a complex number and so cannot match %s map key type", + joinFieldsPath(parts[:idxPart+1]), field.Name, tkey) } - fallthrough + vkey = reflect.ValueOf(c).Convert(tkey) default: return smuggleValue{}, fmt.Errorf( "field %q, %q cannot match unsupported %s map key type", @@ -332,12 +325,6 @@ func getCaster(outType reflect.Type) reflect.Value { return fn } -// Needed for go≤1.12 -// From go1.13, reflect.ValueOf(&ctxerr.Error{…}) works as expected. -func errorInterface(err error) reflect.Value { - return reflect.ValueOf(&err).Elem() -} - // buildCaster returns a function: // // func(in any) (out outType, err error) @@ -362,7 +349,7 @@ func buildCaster(outType reflect.Type, useString bool) reflect.Value { if args[0].IsNil() { return []reflect.Value{ zeroRet, - errorInterface(&ctxerr.Error{ + reflect.ValueOf(&ctxerr.Error{ Message: "incompatible parameter type", Got: types.RawString("nil"), Expected: types.RawString(outType.String() + " or convertible or io.Reader"), @@ -383,11 +370,11 @@ func buildCaster(outType reflect.Type, useString bool) reflect.Value { // Our caller encures Interface() can be called safely switch ta := args[0].Interface().(type) { case io.Reader: - var b bytes.Buffer // as we still support go1.9 + var b bytes.Buffer if _, err := b.ReadFrom(ta); err != nil { return []reflect.Value{ zeroRet, - errorInterface(&ctxerr.Error{ + reflect.ValueOf(&ctxerr.Error{ Message: "an error occurred while reading from io.Reader", Summary: ctxerr.NewSummary(err.Error()), }), @@ -407,7 +394,7 @@ func buildCaster(outType reflect.Type, useString bool) reflect.Value { default: return []reflect.Value{ zeroRet, - errorInterface(&ctxerr.Error{ + reflect.ValueOf(&ctxerr.Error{ Message: "incompatible parameter type", Got: types.RawString(args[0].Type().String()), Expected: types.RawString(outType.String() + " or convertible or io.Reader"), diff --git a/td/td_smuggle_go115.go b/td/td_smuggle_go115.go deleted file mode 100644 index 1432ffb9..00000000 --- a/td/td_smuggle_go115.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) 2021, Maxime Soulé -// All rights reserved. -// -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. - -//go:build go1.15 -// +build go1.15 - -package td - -import "strconv" - -// strconv.ParseComplex is only available from go 1.15. -func init() { - parseComplex = strconv.ParseComplex -} diff --git a/td/td_smuggle_go115_test.go b/td/td_smuggle_go115_test.go deleted file mode 100644 index 20dc28ea..00000000 --- a/td/td_smuggle_go115_test.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) 2021, Maxime Soulé -// All rights reserved. -// -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. - -//go:build go1.15 -// +build go1.15 - -package td_test - -import ( - "fmt" - "testing" - - "github.com/maxatome/go-testdeep/td" -) - -func TestSmuggleFieldsPath_go115(t *testing.T) { - type C struct { - Iface any - } - - got := C{ - Iface: []any{ - map[complex64]any{complex(42, 0): []string{"pipo"}}, - map[complex128]any{complex(42, 0): []string{"pipo"}}, - }, - } - - for i := 0; i < 2; i++ { - checkOK(t, got, td.Smuggle(fmt.Sprintf("Iface[%d][42][0]", i), "pipo")) - checkOK(t, got, td.Smuggle(fmt.Sprintf("Iface[%d][42][0]", i-2), "pipo")) - } -} diff --git a/td/td_smuggle_private_test.go b/td/td_smuggle_private_test.go index 4353c62d..d63ea5ce 100644 --- a/td/td_smuggle_private_test.go +++ b/td/td_smuggle_private_test.go @@ -128,13 +128,10 @@ func TestBuildFieldsPathFn(t *testing.T) { `field "Iface[str]", "str" is not a float and so cannot match float32 map key type`) } - // go1.15 min - if parseComplex != nil { - _, err = fn(Build{Iface: map[complex128]Build{}}) - if test.Error(t, err) { - test.EqualStr(t, err.Error(), - `field "Iface[str]", "str" is not a complex number and so cannot match complex128 map key type`) - } + _, err = fn(Build{Iface: map[complex128]Build{}}) + if test.Error(t, err) { + test.EqualStr(t, err.Error(), + `field "Iface[str]", "str" is not a complex number and so cannot match complex128 map key type`) } _, err = fn(Build{Iface: map[struct{ A int }]Build{}}) @@ -181,24 +178,4 @@ func TestBuildFieldsPathFn(t *testing.T) { test.EqualStr(t, err.Error(), `it is a int, but a map, array or slice is expected`) } - - // Complex map keys are not supported for go<1.15 - saveParseComplex := parseComplex - defer func() { parseComplex = saveParseComplex }() - parseComplex = nil - - fn, err = buildFieldsPathFn("Iface[18].Field") - if test.NoError(t, err) { - _, err = fn(Build{Iface: map[complex64]Build{}}) - if test.Error(t, err) { - test.EqualStr(t, err.Error(), - `field "Iface[18]", "18" cannot match unsupported complex64 map key type`) - } - - _, err = fn(Build{Iface: map[complex128]Build{}}) - if test.Error(t, err) { - test.EqualStr(t, err.Error(), - `field "Iface[18]", "18" cannot match unsupported complex128 map key type`) - } - } } diff --git a/td/td_smuggle_test.go b/td/td_smuggle_test.go index 21843ca0..587ba515 100644 --- a/td/td_smuggle_test.go +++ b/td/td_smuggle_test.go @@ -727,6 +727,23 @@ func TestSmuggleFieldsPath(t *testing.T) { checkOK(t, x, td.Lax(td.Smuggle("PppA", nil))) checkOK(t, x, td.Smuggle("PppA", td.Nil())) + + // + type D struct { + Iface any + } + + got := D{ + Iface: []any{ + map[complex64]any{complex(42, 0): []string{"pipo"}}, + map[complex128]any{complex(42, 0): []string{"pipo"}}, + }, + } + + for i := 0; i < 2; i++ { + checkOK(t, got, td.Smuggle(fmt.Sprintf("Iface[%d][42][0]", i), "pipo")) + checkOK(t, got, td.Smuggle(fmt.Sprintf("Iface[%d][42][0]", i-2), "pipo")) + } } func TestSmuggleTypeBehind(t *testing.T) { diff --git a/td/types_test.go b/td/types_test.go index 0a3e5e6f..bb682555 100644 --- a/td/types_test.go +++ b/td/types_test.go @@ -4,11 +4,6 @@ // This source code is licensed under the BSD-style license found in the // LICENSE file in the root directory of this source tree. -// Work around https://github.com/golang/go/issues/26995 issue -// (corrected in go 1.12). -//go:build go1.12 -// +build go1.12 - package td_test import (