From ddc8675b1ce66e101b874dbfad92c6311bb00341 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20Soul=C3=A9?= Date: Sat, 8 Oct 2022 23:54:01 +0200 Subject: [PATCH] refactor: go1.16 required now, so drop support for go1.9 to go1.15 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime Soulé --- .github/workflows/ci.yml | 14 +-- .golangci.yml | 2 +- README.md | 4 +- helpers/tdhttp/example_test.go | 4 +- helpers/tdhttp/internal/response_test.go | 4 +- helpers/tdhttp/multipart_test.go | 25 ++--- helpers/tdhttp/request_test.go | 8 +- helpers/tdsuite/suite.go | 14 +-- helpers/tdsuite/suite_go114_test.go | 104 ------------------ helpers/tdsuite/suite_test.go | 87 +++++++++++++++ helpers/tdutil/map.go | 71 ++++++++++++ helpers/tdutil/map_go112.go | 86 --------------- helpers/tdutil/map_other.go | 49 --------- ...{map_go112_test.go => map_private_test.go} | 3 - helpers/tdutil/name.go | 3 +- internal/ctxerr/op_error.go | 4 +- internal/json/parser_go113_test.go | 75 ------------- internal/json/parser_test.go | 58 ++++++++++ internal/trace/trace_test.go | 11 +- td/cmp_deeply.go | 3 +- td/example_cmp_test.go | 13 +-- td/example_t_test.go | 13 +-- td/example_test.go | 13 +-- td/t_anchor.go | 2 +- td/t_anchor_go114.go | 16 --- td/t_anchor_other.go | 19 ---- td/t_struct.go | 6 +- td/td_json.go | 18 +-- td/td_json_test.go | 9 +- td/td_map.go | 6 +- td/td_smuggle.go | 35 ++---- td/td_smuggle_go115.go | 17 --- td/td_smuggle_go115_test.go | 35 ------ td/td_smuggle_private_test.go | 31 +----- td/td_smuggle_test.go | 17 +++ td/types_test.go | 5 - 36 files changed, 325 insertions(+), 559 deletions(-) delete mode 100644 helpers/tdsuite/suite_go114_test.go delete mode 100644 helpers/tdutil/map_go112.go delete mode 100644 helpers/tdutil/map_other.go rename helpers/tdutil/{map_go112_test.go => map_private_test.go} (97%) delete mode 100644 internal/json/parser_go113_test.go delete mode 100644 td/t_anchor_go114.go delete mode 100644 td/t_anchor_other.go delete mode 100644 td/td_smuggle_go115.go delete mode 100644 td/td_smuggle_go115_test.go 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/.golangci.yml b/.golangci.yml index bea7c93f..26b4bd73 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,2 +1,2 @@ run: - go: '1.9' + go: '1.16' diff --git a/README.md b/README.md index 6206697d..442459a4 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) @@ -203,7 +203,7 @@ details.** ## Installation ```sh -$ go get -u github.com/maxatome/go-testdeep +$ go get github.com/maxatome/go-testdeep ``` diff --git a/helpers/tdhttp/example_test.go b/helpers/tdhttp/example_test.go index 16ba3d11..7f19a3e4 100644 --- a/helpers/tdhttp/example_test.go +++ b/helpers/tdhttp/example_test.go @@ -10,7 +10,7 @@ import ( "encoding/json" "encoding/xml" "fmt" - "io/ioutil" + "io" "net/http" "net/url" "strconv" @@ -103,7 +103,7 @@ func Example() { return } case "application/x-www-form-urlencoded": - b, err := ioutil.ReadAll(req.Body) + b, err := io.ReadAll(req.Body) if err != nil { http.Error(w, "Bad request", http.StatusBadRequest) return diff --git a/helpers/tdhttp/internal/response_test.go b/helpers/tdhttp/internal/response_test.go index 2340aa74..cf4b85b3 100644 --- a/helpers/tdhttp/internal/response_test.go +++ b/helpers/tdhttp/internal/response_test.go @@ -8,7 +8,7 @@ package internal_test import ( "bytes" - "io/ioutil" + "io" "net/http" "testing" @@ -28,7 +28,7 @@ func newResponse(body string) *http.Response { "A": []string{"foo"}, "B": []string{"bar"}, }, - Body: ioutil.NopCloser(bytes.NewBufferString(body)), + Body: io.NopCloser(bytes.NewBufferString(body)), } } diff --git a/helpers/tdhttp/multipart_test.go b/helpers/tdhttp/multipart_test.go index a89229dc..728bb58a 100644 --- a/helpers/tdhttp/multipart_test.go +++ b/helpers/tdhttp/multipart_test.go @@ -9,7 +9,6 @@ package tdhttp_test import ( "bytes" "io" - "io/ioutil" "mime/multipart" "net/http" "os" @@ -38,7 +37,7 @@ func TestMultipartPart(t *testing.T) { } // Full empty - b, err := ioutil.ReadAll(&tdhttp.MultipartPart{}) + b, err := io.ReadAll(&tdhttp.MultipartPart{}) assert.CmpNoError(err) assert.Len(b, 0) @@ -143,11 +142,11 @@ hey! yo!`) // With file name - dir, err := ioutil.TempDir("", "multipart") + dir, err := os.MkdirTemp("", "multipart") require.CmpNoError(err) defer os.RemoveAll(dir) filePath := filepath.Join(dir, "body.txt") - require.CmpNoError(ioutil.WriteFile(filePath, []byte("hey!\nyo!"), 0666)) + require.CmpNoError(os.WriteFile(filePath, []byte("hey!\nyo!"), 0666)) check(tdhttp.NewMultipartPartFile("pipo", filePath), `Content-Disposition: form-data; name="pipo"; filename="body.txt"%CR @@ -165,7 +164,7 @@ hey! yo!`) // Error during os.Open - _, err = ioutil.ReadAll( + _, err = io.ReadAll( tdhttp.NewMultipartPartFile("pipo", filepath.Join(dir, "unknown.xxx")), ) assert.CmpError(err) @@ -174,11 +173,11 @@ yo!`) func TestMultipartBody(t *testing.T) { assert, require := td.AssertRequire(t) - dir, err := ioutil.TempDir("", "multipart") + dir, err := os.MkdirTemp("", "multipart") require.CmpNoError(err) defer os.RemoveAll(dir) filePath := filepath.Join(dir, "body.txt") - require.CmpNoError(ioutil.WriteFile(filePath, []byte("hey!\nyo!"), 0666)) + require.CmpNoError(os.WriteFile(filePath, []byte("hey!\nyo!"), 0666)) for _, boundary := range []struct{ in, out string }{ {in: "", out: "go-testdeep-42"}, @@ -251,7 +250,7 @@ Content-Disposition: form-data; name="io"%CR if assert.CmpNoError(err) { assert.Cmp(part.FormName(), "pipo") assert.Cmp(part.FileName(), "") - assert.Smuggle(part, ioutil.ReadAll, td.String("pipo!\nbingo!")) + assert.Smuggle(part, io.ReadAll, td.String("pipo!\nbingo!")) } // 1 @@ -259,7 +258,7 @@ Content-Disposition: form-data; name="io"%CR if assert.CmpNoError(err) { assert.Cmp(part.FormName(), "file") assert.Cmp(part.FileName(), "body.txt") - assert.Smuggle(part, ioutil.ReadAll, td.String("hey!\nyo!")) + assert.Smuggle(part, io.ReadAll, td.String("hey!\nyo!")) } // 2 @@ -267,7 +266,7 @@ Content-Disposition: form-data; name="io"%CR if assert.CmpNoError(err) { assert.Cmp(part.FormName(), "string") assert.Cmp(part.FileName(), "") - assert.Smuggle(part, ioutil.ReadAll, td.String("zip!\nzap!")) + assert.Smuggle(part, io.ReadAll, td.String("zip!\nzap!")) } // 3 @@ -275,7 +274,7 @@ Content-Disposition: form-data; name="io"%CR if assert.CmpNoError(err) { assert.Cmp(part.FormName(), "bytes") assert.Cmp(part.FileName(), "") - assert.Smuggle(part, ioutil.ReadAll, td.String(`{"ola":"hello"}`)) + assert.Smuggle(part, io.ReadAll, td.String(`{"ola":"hello"}`)) } // 4 @@ -283,7 +282,7 @@ Content-Disposition: form-data; name="io"%CR if assert.CmpNoError(err) { assert.Cmp(part.FormName(), "io") assert.Cmp(part.FileName(), "") - assert.Smuggle(part, ioutil.ReadAll, td.String("")) + assert.Smuggle(part, io.ReadAll, td.String("")) } // 5 @@ -291,7 +290,7 @@ Content-Disposition: form-data; name="io"%CR if assert.CmpNoError(err) { assert.Cmp(part.FormName(), "") assert.Cmp(part.FileName(), "") - assert.Smuggle(part, ioutil.ReadAll, td.String("")) + assert.Smuggle(part, io.ReadAll, td.String("")) } // EOF diff --git a/helpers/tdhttp/request_test.go b/helpers/tdhttp/request_test.go index e0242cab..cc49c43e 100644 --- a/helpers/tdhttp/request_test.go +++ b/helpers/tdhttp/request_test.go @@ -7,7 +7,7 @@ package tdhttp_test import ( - "io/ioutil" + "io" "net/http" "net/url" "testing" @@ -251,7 +251,7 @@ func TestNewRequest(tt *testing.T) { td.StructFields{ "URL": td.String("/path"), "Body": td.Smuggle( - ioutil.ReadAll, + io.ReadAll, []byte("param1=val1¶m1=val2¶m2=zip"), ), })) @@ -336,7 +336,7 @@ func TestNewJSONRequest(tt *testing.T) { t.String(req.Header.Get("Foo"), "Bar") t.String(req.Header.Get("Zip"), "Test") - body, err := ioutil.ReadAll(req.Body) + body, err := io.ReadAll(req.Body) if t.CmpNoError(err, "read request body") { t.String(string(body), `{"name":"Bob"}`) } @@ -445,7 +445,7 @@ func TestNewXMLRequest(tt *testing.T) { t.String(req.Header.Get("Foo"), "Bar") t.String(req.Header.Get("Zip"), "Test") - body, err := ioutil.ReadAll(req.Body) + body, err := io.ReadAll(req.Body) if t.CmpNoError(err, "read request body") { t.String(string(body), `Bob`) } 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/internal/trace/trace_test.go b/internal/trace/trace_test.go index 13c59667..7f2e8803 100644 --- a/internal/trace/trace_test.go +++ b/internal/trace/trace_test.go @@ -8,7 +8,6 @@ package trace_test import ( "go/build" - "io/ioutil" "os" "path/filepath" "runtime" @@ -38,7 +37,7 @@ func TestIgnorePackage(t *testing.T) { } func TestFindGoModDir(t *testing.T) { - tmp, err := ioutil.TempDir("", "go-testdeep") + tmp, err := os.MkdirTemp("", "go-testdeep") if err != nil { t.Fatalf("TempDir() failed: %s", err) } @@ -55,7 +54,7 @@ func TestFindGoModDir(t *testing.T) { t.Run("/tmp/.../a/b/c/go.mod", func(t *testing.T) { goMod := filepath.Join(tmp, "a", "b", "c", "go.mod") - err := ioutil.WriteFile(goMod, nil, 0644) + err := os.WriteFile(goMod, nil, 0644) if err != nil { t.Fatalf("WriteFile(%s) failed: %s", goMod, err) } @@ -74,7 +73,7 @@ func TestFindGoModDir(t *testing.T) { if !os.IsNotExist(err) { t.Fatalf("Stat(%s) failed: %s", goMod, err) } - err := ioutil.WriteFile(goMod, nil, 0644) + err := os.WriteFile(goMod, nil, 0644) if err != nil { t.Fatalf("WriteFile(%s) failed: %s", goMod, err) } @@ -86,7 +85,7 @@ func TestFindGoModDir(t *testing.T) { } func TestFindGoModDirLinks(t *testing.T) { - tmp, err := ioutil.TempDir("", "go-testdeep") + tmp, err := os.MkdirTemp("", "go-testdeep") if err != nil { t.Fatalf("TempDir() failed: %s", err) } @@ -108,7 +107,7 @@ func TestFindGoModDirLinks(t *testing.T) { goMod := filepath.Join(goModDir, "go.mod") - err = ioutil.WriteFile(goMod, nil, 0644) + err = os.WriteFile(goMod, nil, 0644) if err != nil { t.Fatalf("WriteFile(%s) failed: %s", goMod, err) } 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/example_cmp_test.go b/td/example_cmp_test.go index 0fe33029..9de99d3a 100644 --- a/td/example_cmp_test.go +++ b/td/example_cmp_test.go @@ -13,7 +13,6 @@ import ( "encoding/json" "errors" "fmt" - "io/ioutil" "math" "os" "regexp" @@ -1277,14 +1276,14 @@ func ExampleCmpJSON_file() { Gender: "male", } - tmpDir, err := ioutil.TempDir("", "") + tmpDir, err := os.MkdirTemp("", "") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpDir) // clean up filename := tmpDir + "/test.json" - if err = ioutil.WriteFile(filename, []byte(` + if err = os.WriteFile(filename, []byte(` { "fullname": "$name", "age": "$age", @@ -3461,14 +3460,14 @@ func ExampleCmpSubJSONOf_file() { Gender: "male", } - tmpDir, err := ioutil.TempDir("", "") + tmpDir, err := os.MkdirTemp("", "") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpDir) // clean up filename := tmpDir + "/test.json" - if err = ioutil.WriteFile(filename, []byte(` + if err = os.WriteFile(filename, []byte(` { "fullname": "$name", "age": "$age", @@ -3690,14 +3689,14 @@ func ExampleCmpSuperJSONOf_file() { Zip: 666, } - tmpDir, err := ioutil.TempDir("", "") + tmpDir, err := os.MkdirTemp("", "") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpDir) // clean up filename := tmpDir + "/test.json" - if err = ioutil.WriteFile(filename, []byte(` + if err = os.WriteFile(filename, []byte(` { "fullname": "$name", "age": "$age", diff --git a/td/example_t_test.go b/td/example_t_test.go index 8289490d..144e41a8 100644 --- a/td/example_t_test.go +++ b/td/example_t_test.go @@ -13,7 +13,6 @@ import ( "encoding/json" "errors" "fmt" - "io/ioutil" "math" "os" "regexp" @@ -1277,14 +1276,14 @@ func ExampleT_JSON_file() { Gender: "male", } - tmpDir, err := ioutil.TempDir("", "") + tmpDir, err := os.MkdirTemp("", "") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpDir) // clean up filename := tmpDir + "/test.json" - if err = ioutil.WriteFile(filename, []byte(` + if err = os.WriteFile(filename, []byte(` { "fullname": "$name", "age": "$age", @@ -3461,14 +3460,14 @@ func ExampleT_SubJSONOf_file() { Gender: "male", } - tmpDir, err := ioutil.TempDir("", "") + tmpDir, err := os.MkdirTemp("", "") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpDir) // clean up filename := tmpDir + "/test.json" - if err = ioutil.WriteFile(filename, []byte(` + if err = os.WriteFile(filename, []byte(` { "fullname": "$name", "age": "$age", @@ -3690,14 +3689,14 @@ func ExampleT_SuperJSONOf_file() { Zip: 666, } - tmpDir, err := ioutil.TempDir("", "") + tmpDir, err := os.MkdirTemp("", "") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpDir) // clean up filename := tmpDir + "/test.json" - if err = ioutil.WriteFile(filename, []byte(` + if err = os.WriteFile(filename, []byte(` { "fullname": "$name", "age": "$age", diff --git a/td/example_test.go b/td/example_test.go index 3bd07ba8..cbb1ca9f 100644 --- a/td/example_test.go +++ b/td/example_test.go @@ -11,7 +11,6 @@ import ( "encoding/json" "errors" "fmt" - "io/ioutil" "math" "os" "regexp" @@ -1478,14 +1477,14 @@ func ExampleJSON_file() { Gender: "male", } - tmpDir, err := ioutil.TempDir("", "") + tmpDir, err := os.MkdirTemp("", "") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpDir) // clean up filename := tmpDir + "/test.json" - if err = ioutil.WriteFile(filename, []byte(` + if err = os.WriteFile(filename, []byte(` { "fullname": "$name", "age": "$age", @@ -4108,14 +4107,14 @@ func ExampleSubJSONOf_file() { Gender: "male", } - tmpDir, err := ioutil.TempDir("", "") + tmpDir, err := os.MkdirTemp("", "") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpDir) // clean up filename := tmpDir + "/test.json" - if err = ioutil.WriteFile(filename, []byte(` + if err = os.WriteFile(filename, []byte(` { "fullname": "$name", "age": "$age", @@ -4364,14 +4363,14 @@ func ExampleSuperJSONOf_file() { Zip: 666, } - tmpDir, err := ioutil.TempDir("", "") + tmpDir, err := os.MkdirTemp("", "") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpDir) // clean up filename := tmpDir + "/test.json" - if err = ioutil.WriteFile(filename, []byte(` + if err = os.WriteFile(filename, []byte(` { "fullname": "$name", "age": "$age", 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_json.go b/td/td_json.go index 9c4eb8df..d90cdd80 100644 --- a/td/td_json.go +++ b/td/td_json.go @@ -12,7 +12,7 @@ import ( "errors" "fmt" "io" - "io/ioutil" + "os" "reflect" "strings" @@ -110,7 +110,7 @@ func (u tdJSONUnmarshaler) unmarshal(expectedJSON any, params []any) (any, *ctxe // a JSON content) if strings.HasSuffix(data, ".json") { // It could be a file name, try to read from it - b, err = ioutil.ReadFile(data) + b, err = os.ReadFile(data) if err != nil { return nil, ctxerr.OpBad(u.Func, "JSON file %s cannot be read: %s", data, err) } @@ -122,7 +122,7 @@ func (u tdJSONUnmarshaler) unmarshal(expectedJSON any, params []any) (any, *ctxe b = data case io.Reader: - b, err = ioutil.ReadAll(data) + b, err = io.ReadAll(data) if err != nil { return nil, ctxerr.OpBad(u.Func, "JSON read error: %s", err) } @@ -489,9 +489,9 @@ func jsonify(ctx ctxerr.Context, got reflect.Value) (any, *ctxerr.Error) { // // - string containing JSON data like `{"fullname":"Bob","age":42}` // - string containing a JSON filename, ending with ".json" (its -// content is [ioutil.ReadFile] before unmarshaling) +// content is [os.ReadFile] before unmarshaling) // - []byte containing JSON data -// - [io.Reader] stream containing JSON data (is [ioutil.ReadAll] +// - [io.Reader] stream containing JSON data (is [io.ReadAll] // before unmarshaling) // // expectedJSON JSON value can contain placeholders. The params @@ -740,9 +740,9 @@ var _ TestDeep = &tdMapJSON{} // // - string containing JSON data like `{"fullname":"Bob","age":42}` // - string containing a JSON filename, ending with ".json" (its -// content is [ioutil.ReadFile] before unmarshaling) +// content is [os.ReadFile] before unmarshaling) // - []byte containing JSON data -// - [io.Reader] stream containing JSON data (is [ioutil.ReadAll] before +// - [io.Reader] stream containing JSON data (is [io.ReadAll] before // unmarshaling) // // JSON data contained in expectedJSON must be a JSON object/map @@ -962,9 +962,9 @@ func SubJSONOf(expectedJSON any, params ...any) TestDeep { // // - string containing JSON data like `{"fullname":"Bob","age":42}` // - string containing a JSON filename, ending with ".json" (its -// content is [ioutil.ReadFile] before unmarshaling) +// content is [os.ReadFile] before unmarshaling) // - []byte containing JSON data -// - [io.Reader] stream containing JSON data (is [ioutil.ReadAll] before +// - [io.Reader] stream containing JSON data (is [io.ReadAll] before // unmarshaling) // // JSON data contained in expectedJSON must be a JSON object/map diff --git a/td/td_json_test.go b/td/td_json_test.go index 3ad4ff39..430b3535 100644 --- a/td/td_json_test.go +++ b/td/td_json_test.go @@ -9,7 +9,6 @@ package td_test import ( "encoding/json" "errors" - "io/ioutil" "os" "reflect" "testing" @@ -190,14 +189,10 @@ func TestJSON(t *testing.T) { // // Loading a file - tmpDir, err := ioutil.TempDir("", "") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpDir) // clean up + tmpDir := t.TempDir() filename := tmpDir + "/test.json" - err = ioutil.WriteFile( + err := os.WriteFile( filename, []byte(`{"name":$name,"age":$1,"gender":$^NotEmpty}`), 0644) if err != nil { t.Fatal(err) 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 (