From 6c976a080c763173668420e8783e1d36d193fe4e Mon Sep 17 00:00:00 2001 From: shamaton Date: Thu, 1 Aug 2024 23:54:50 +0900 Subject: [PATCH 01/41] add string tests --- internal/common/testutil/testutil.go | 45 +++++++++++ internal/stream/decoding/string_test.go | 100 ++++++++++++++++++++++++ 2 files changed, 145 insertions(+) create mode 100644 internal/common/testutil/testutil.go create mode 100644 internal/stream/decoding/string_test.go diff --git a/internal/common/testutil/testutil.go b/internal/common/testutil/testutil.go new file mode 100644 index 0000000..946bdd5 --- /dev/null +++ b/internal/common/testutil/testutil.go @@ -0,0 +1,45 @@ +package testutil + +import ( + "errors" + "strings" + "testing" +) + +func NoError(t *testing.T, err error) { + t.Helper() + if err != nil { + t.Fatal(err) + } +} + +func Error(t *testing.T, err error) { + t.Helper() + if err == nil { + t.Fatal(err) + } +} + +func IsError(t *testing.T, actual, expected error) { + t.Helper() + if !errors.Is(actual, expected) { + t.Fatalf("not equal. actual: %v, expected: %v", actual, expected) + } +} + +func ErrorContains(t *testing.T, err error, errStr string) { + t.Helper() + if err == nil { + t.Fatal("error should occur") + } + if !strings.Contains(err.Error(), errStr) { + t.Fatalf("error does not contain '%s'. err: %v", errStr, err) + } +} + +func Equal[T comparable](t *testing.T, actual, expected T) { + t.Helper() + if actual != expected { + t.Fatalf("not equal. actual: %v, expected: %v", actual, expected) + } +} diff --git a/internal/stream/decoding/string_test.go b/internal/stream/decoding/string_test.go new file mode 100644 index 0000000..e3c7ea5 --- /dev/null +++ b/internal/stream/decoding/string_test.go @@ -0,0 +1,100 @@ +package decoding + +import ( + "bytes" + "errors" + "github.com/shamaton/msgpack/v2/def" + "github.com/shamaton/msgpack/v2/internal/common" + "github.com/shamaton/msgpack/v2/internal/common/testutil" + "io" + "math" + "reflect" + "testing" +) + +var errReaderErr = errors.New("reader error") + +type errReader struct{} + +func (errReader) Read(p []byte) (n int, err error) { + return 0, errReaderErr +} + +func Test_stringByteLength(t *testing.T) { + testcases := []struct { + name string + code byte + length int + expected int + errSkip bool + }{ + { + name: "FixStr", + code: def.FixStr + 1, + expected: 1, + errSkip: true, + }, + { + name: "Str8", + code: def.Str8, + length: 1, + expected: math.MaxUint8, + }, + { + name: "Str16", + code: def.Str16, + length: 2, + expected: math.MaxUint16, + }, + { + name: "Str32", + code: def.Str32, + length: 4, + expected: math.MaxUint32, + }, + { + name: "Nil", + code: def.Nil, + expected: 0, + errSkip: true, + }, + } + + for _, tc := range testcases { + t.Run(tc.name+"", func(t *testing.T) { + t.Run("ng", func(t *testing.T) { + if tc.errSkip { + t.Log("this testcase is skipped by skip flag") + return + } + d := decoder{ + r: &errReader{}, + buf: common.GetBuffer(), + } + defer common.PutBuffer(d.buf) + _, err := d.stringByteLength(tc.code, reflect.String) + testutil.IsError(t, err, errReaderErr) + }) + t.Run("ok", func(t *testing.T) { + data := make([]byte, tc.length) + for i := range data { + data[i] = 0xff + } + + d := decoder{ + r: bytes.NewReader(data), + buf: common.GetBuffer(), + } + defer common.PutBuffer(d.buf) + v, err := d.stringByteLength(tc.code, reflect.String) + testutil.NoError(t, err) + testutil.Equal(t, v, tc.expected) + + p := make([]byte, 1) + n, err := d.r.Read(p) + testutil.IsError(t, err, io.EOF) + testutil.Equal(t, n, 0) + }) + }) + } +} From 35822e515aca1372dfbbc4e76605deb6c10a2f61 Mon Sep 17 00:00:00 2001 From: shamaton Date: Fri, 2 Aug 2024 08:29:19 +0900 Subject: [PATCH 02/41] add decording string tests --- internal/stream/decoding/string_test.go | 28 ++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/internal/stream/decoding/string_test.go b/internal/stream/decoding/string_test.go index e3c7ea5..884cd36 100644 --- a/internal/stream/decoding/string_test.go +++ b/internal/stream/decoding/string_test.go @@ -3,13 +3,14 @@ package decoding import ( "bytes" "errors" - "github.com/shamaton/msgpack/v2/def" - "github.com/shamaton/msgpack/v2/internal/common" - "github.com/shamaton/msgpack/v2/internal/common/testutil" "io" "math" "reflect" "testing" + + "github.com/shamaton/msgpack/v2/def" + "github.com/shamaton/msgpack/v2/internal/common" + "github.com/shamaton/msgpack/v2/internal/common/testutil" ) var errReaderErr = errors.New("reader error") @@ -98,3 +99,24 @@ func Test_stringByteLength(t *testing.T) { }) } } + +func Test_asString(t *testing.T) { + t.Run("read error", func(t *testing.T) { + d := decoder{ + r: &errReader{}, + buf: common.GetBuffer(), + } + v, err := d.asString(reflect.String) + testutil.IsError(t, err, errReaderErr) + testutil.Equal(t, v, emptyString) + }) + t.Run("ok", func(t *testing.T) { + d := decoder{ + r: bytes.NewReader([]byte{def.FixStr + 1, 'a'}), + buf: common.GetBuffer(), + } + v, err := d.asString(reflect.String) + testutil.NoError(t, err) + testutil.Equal(t, v, "a") + }) +} From 393f8753b72785e00fce5f8e326c9d2948a519b6 Mon Sep 17 00:00:00 2001 From: shamaton Date: Fri, 2 Aug 2024 22:15:27 +0900 Subject: [PATCH 03/41] add decoding string test --- internal/common/testutil/reader.go | 15 +++++++ internal/common/testutil/testutil.go | 10 +++++ internal/stream/decoding/string_test.go | 54 +++++++++++++++---------- 3 files changed, 58 insertions(+), 21 deletions(-) create mode 100644 internal/common/testutil/reader.go diff --git a/internal/common/testutil/reader.go b/internal/common/testutil/reader.go new file mode 100644 index 0000000..bffc5a4 --- /dev/null +++ b/internal/common/testutil/reader.go @@ -0,0 +1,15 @@ +package testutil + +import "errors" + +var ErrReaderErr = errors.New("reader error") + +type ErrReader struct{} + +func NewErrReader() *ErrReader { + return &ErrReader{} +} + +func (ErrReader) Read(_ []byte) (int, error) { + return 0, ErrReaderErr +} diff --git a/internal/common/testutil/testutil.go b/internal/common/testutil/testutil.go index 946bdd5..f1d4405 100644 --- a/internal/common/testutil/testutil.go +++ b/internal/common/testutil/testutil.go @@ -43,3 +43,13 @@ func Equal[T comparable](t *testing.T, actual, expected T) { t.Fatalf("not equal. actual: %v, expected: %v", actual, expected) } } + +func EqualSlice[T comparable](t *testing.T, actual, expected []T) { + t.Helper() + if len(actual) != len(expected) { + t.Fatalf("diffrent length. actual: %v, expected: %v", actual, expected) + } + for i := range actual { + Equal[T](t, actual[i], expected[i]) + } +} diff --git a/internal/stream/decoding/string_test.go b/internal/stream/decoding/string_test.go index 884cd36..6c3ea6e 100644 --- a/internal/stream/decoding/string_test.go +++ b/internal/stream/decoding/string_test.go @@ -2,7 +2,6 @@ package decoding import ( "bytes" - "errors" "io" "math" "reflect" @@ -10,17 +9,9 @@ import ( "github.com/shamaton/msgpack/v2/def" "github.com/shamaton/msgpack/v2/internal/common" - "github.com/shamaton/msgpack/v2/internal/common/testutil" + tu "github.com/shamaton/msgpack/v2/internal/common/testutil" ) -var errReaderErr = errors.New("reader error") - -type errReader struct{} - -func (errReader) Read(p []byte) (n int, err error) { - return 0, errReaderErr -} - func Test_stringByteLength(t *testing.T) { testcases := []struct { name string @@ -69,12 +60,12 @@ func Test_stringByteLength(t *testing.T) { return } d := decoder{ - r: &errReader{}, + r: tu.NewErrReader(), buf: common.GetBuffer(), } defer common.PutBuffer(d.buf) _, err := d.stringByteLength(tc.code, reflect.String) - testutil.IsError(t, err, errReaderErr) + tu.IsError(t, err, tu.ErrReaderErr) }) t.Run("ok", func(t *testing.T) { data := make([]byte, tc.length) @@ -88,13 +79,13 @@ func Test_stringByteLength(t *testing.T) { } defer common.PutBuffer(d.buf) v, err := d.stringByteLength(tc.code, reflect.String) - testutil.NoError(t, err) - testutil.Equal(t, v, tc.expected) + tu.NoError(t, err) + tu.Equal(t, v, tc.expected) p := make([]byte, 1) n, err := d.r.Read(p) - testutil.IsError(t, err, io.EOF) - testutil.Equal(t, n, 0) + tu.IsError(t, err, io.EOF) + tu.Equal(t, n, 0) }) }) } @@ -103,12 +94,12 @@ func Test_stringByteLength(t *testing.T) { func Test_asString(t *testing.T) { t.Run("read error", func(t *testing.T) { d := decoder{ - r: &errReader{}, + r: tu.NewErrReader(), buf: common.GetBuffer(), } v, err := d.asString(reflect.String) - testutil.IsError(t, err, errReaderErr) - testutil.Equal(t, v, emptyString) + tu.IsError(t, err, tu.ErrReaderErr) + tu.Equal(t, v, emptyString) }) t.Run("ok", func(t *testing.T) { d := decoder{ @@ -116,7 +107,28 @@ func Test_asString(t *testing.T) { buf: common.GetBuffer(), } v, err := d.asString(reflect.String) - testutil.NoError(t, err) - testutil.Equal(t, v, "a") + tu.NoError(t, err) + tu.Equal(t, v, "a") + }) +} + +func Test_asStringByte(t *testing.T) { + t.Run("read error", func(t *testing.T) { + d := decoder{ + r: tu.NewErrReader(), + buf: common.GetBuffer(), + } + v, err := d.asStringByte(reflect.String) + tu.IsError(t, err, tu.ErrReaderErr) + tu.EqualSlice(t, v, emptyBytes) + }) + t.Run("ok", func(t *testing.T) { + d := decoder{ + r: bytes.NewReader([]byte{def.FixStr + 1, 'a'}), + buf: common.GetBuffer(), + } + v, err := d.asStringByte(reflect.String) + tu.NoError(t, err) + tu.EqualSlice(t, v, []byte("a")) }) } From 7710f56cef18f106484a99c21ded03e0173f706a Mon Sep 17 00:00:00 2001 From: shamaton Date: Fri, 2 Aug 2024 22:53:18 +0900 Subject: [PATCH 04/41] add decoding uint tests --- internal/stream/decoding/uint_test.go | 137 ++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 internal/stream/decoding/uint_test.go diff --git a/internal/stream/decoding/uint_test.go b/internal/stream/decoding/uint_test.go new file mode 100644 index 0000000..f596740 --- /dev/null +++ b/internal/stream/decoding/uint_test.go @@ -0,0 +1,137 @@ +package decoding + +import ( + "bytes" + "io" + "math" + "reflect" + "testing" + + "github.com/shamaton/msgpack/v2/def" + "github.com/shamaton/msgpack/v2/internal/common" + tu "github.com/shamaton/msgpack/v2/internal/common/testutil" +) + +func Test_asUint(t *testing.T) { + t.Run("read error", func(t *testing.T) { + d := decoder{ + r: tu.NewErrReader(), + buf: common.GetBuffer(), + } + v, err := d.asUint(reflect.Uint) + tu.IsError(t, err, tu.ErrReaderErr) + tu.Equal(t, v, 0) + }) + t.Run("ok", func(t *testing.T) { + d := decoder{ + r: bytes.NewReader([]byte{1}), + buf: common.GetBuffer(), + } + v, err := d.asUint(reflect.Uint) + tu.NoError(t, err) + tu.Equal(t, v, 1) + }) +} + +func Test_asUintWithCode(t *testing.T) { + testcases := []struct { + name string + code byte + length int + expected uint64 + errSkip bool + }{ + { + name: "Uint8", + code: def.Uint8, + length: 1, + expected: math.MaxUint8, + }, + { + name: "Int8", + code: def.Int8, + length: 1, + expected: math.MaxUint64, + }, + { + name: "Uint16", + code: def.Uint16, + length: 2, + expected: math.MaxUint16, + }, + { + name: "Int16", + code: def.Int16, + length: 2, + expected: math.MaxUint64, + }, + { + name: "Uint32", + code: def.Uint32, + length: 4, + expected: math.MaxUint32, + }, + { + name: "Int32", + code: def.Int32, + length: 4, + expected: math.MaxUint64, + }, + { + name: "Uint64", + code: def.Uint64, + length: 8, + expected: math.MaxUint64, + }, + { + name: "Int64", + code: def.Int64, + length: 8, + expected: math.MaxUint64, + }, + { + name: "Nil", + code: def.Nil, + expected: 0, + errSkip: true, + }, + } + + for _, tc := range testcases { + t.Run(tc.name+"", func(t *testing.T) { + t.Run("ng", func(t *testing.T) { + if tc.errSkip { + t.Log("this testcase is skipped by skip flag") + return + } + d := decoder{ + r: tu.NewErrReader(), + buf: common.GetBuffer(), + } + defer common.PutBuffer(d.buf) + _, err := d.asUintWithCode(tc.code, reflect.String) + tu.IsError(t, err, tu.ErrReaderErr) + }) + t.Run("ok", func(t *testing.T) { + data := make([]byte, tc.length) + for i := range data { + data[i] = 0xff + } + + d := decoder{ + r: bytes.NewReader(data), + buf: common.GetBuffer(), + } + defer common.PutBuffer(d.buf) + v, err := d.asUintWithCode(tc.code, reflect.String) + tu.NoError(t, err) + tu.Equal(t, v, tc.expected) + + p := make([]byte, 1) + n, err := d.r.Read(p) + tu.IsError(t, err, io.EOF) + tu.Equal(t, n, 0) + }) + }) + } +} From 8bbffc9dd3e5b59314758c3b53b52ee0bc76a3af Mon Sep 17 00:00:00 2001 From: shamaton Date: Sat, 3 Aug 2024 22:32:00 +0900 Subject: [PATCH 05/41] add decoding ext tests --- internal/common/testutil/reader.go | 29 ++- internal/stream/decoding/ext_test.go | 260 +++++++++++++++++++++++++++ 2 files changed, 288 insertions(+), 1 deletion(-) create mode 100644 internal/stream/decoding/ext_test.go diff --git a/internal/common/testutil/reader.go b/internal/common/testutil/reader.go index bffc5a4..84d3c17 100644 --- a/internal/common/testutil/reader.go +++ b/internal/common/testutil/reader.go @@ -1,6 +1,9 @@ package testutil -import "errors" +import ( + "errors" + "io" +) var ErrReaderErr = errors.New("reader error") @@ -13,3 +16,27 @@ func NewErrReader() *ErrReader { func (ErrReader) Read(_ []byte) (int, error) { return 0, ErrReaderErr } + +type TestReader struct { + s []byte + i int64 // current reading index + count int +} + +func NewTestReader(b []byte) *TestReader { + return &TestReader{s: b, i: 0, count: 0} +} + +func (r *TestReader) Read(b []byte) (n int, err error) { + if r.i >= int64(len(r.s)) { + return 0, io.EOF + } + n = copy(b, r.s[r.i:]) + r.i += int64(n) + r.count++ + return +} + +func (r *TestReader) Count() int { + return r.count +} diff --git a/internal/stream/decoding/ext_test.go b/internal/stream/decoding/ext_test.go new file mode 100644 index 0000000..3f1ff17 --- /dev/null +++ b/internal/stream/decoding/ext_test.go @@ -0,0 +1,260 @@ +package decoding + +import ( + "io" + "testing" + + "github.com/shamaton/msgpack/v2/def" + + "github.com/shamaton/msgpack/v2/internal/common" + + tu "github.com/shamaton/msgpack/v2/internal/common/testutil" + "github.com/shamaton/msgpack/v2/time" +) + +func Test_AddExtDecoder(t *testing.T) { + t.Run("ignore", func(t *testing.T) { + AddExtDecoder(time.StreamDecoder) + tu.Equal(t, len(extCoders), 1) + }) +} + +func Test_RemoveExtDecoder(t *testing.T) { + t.Run("ignore", func(t *testing.T) { + RemoveExtDecoder(time.StreamDecoder) + tu.Equal(t, len(extCoders), 1) + }) +} + +func Test_readIfExtType(t *testing.T) { + testcases := []struct { + name string + code byte + r []byte + typ int8 + data []byte + err error + count int + }{ + { + name: "Fixext1.error.type", + code: def.Fixext1, + r: []byte{}, + err: io.EOF, + count: 0, + }, + { + name: "Fixext1.error.data", + code: def.Fixext1, + r: []byte{1}, + err: io.EOF, + count: 1, + }, + { + name: "Fixext1.ok", + code: def.Fixext1, + r: []byte{1, 2}, + typ: 1, + data: []byte{2}, + count: 2, + }, + + { + name: "Fixext2.error.type", + code: def.Fixext2, + r: []byte{}, + err: io.EOF, + count: 0, + }, + { + name: "Fixext2.error.data", + code: def.Fixext2, + r: []byte{2}, + err: io.EOF, + count: 1, + }, + { + name: "Fixext2.ok", + code: def.Fixext2, + r: []byte{2, 3, 4}, + typ: 2, + data: []byte{3, 4}, + count: 2, + }, + + { + name: "Fixext4.error.type", + code: def.Fixext4, + r: []byte{}, + err: io.EOF, + count: 0, + }, + { + name: "Fixext4.error.data", + code: def.Fixext4, + r: []byte{4}, + err: io.EOF, + count: 1, + }, + { + name: "Fixext4.ok", + code: def.Fixext4, + r: []byte{4, 5, 6, 7, 8}, + typ: 4, + data: []byte{5, 6, 7, 8}, + count: 2, + }, + + { + name: "Fixext8.error.type", + code: def.Fixext8, + r: []byte{}, + err: io.EOF, + count: 0, + }, + { + name: "Fixext8.error.data", + code: def.Fixext8, + r: []byte{8}, + err: io.EOF, + count: 1, + }, + { + name: "Fixext8.ok", + code: def.Fixext8, + r: []byte{8, 1, 2, 3, 4, 5, 6, 7, 8}, + typ: 8, + data: []byte{1, 2, 3, 4, 5, 6, 7, 8}, + count: 2, + }, + + { + name: "Fixext16.error.type", + code: def.Fixext16, + r: []byte{}, + err: io.EOF, + count: 0, + }, + { + name: "Fixext16.error.data", + code: def.Fixext16, + r: []byte{16}, + err: io.EOF, + count: 1, + }, + { + name: "Fixext16.ok", + code: def.Fixext16, + r: []byte{16, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, + typ: 16, + data: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, + count: 2, + }, + + { + name: "Ext8.error.size", + code: def.Ext8, + r: []byte{}, + err: io.EOF, + count: 0, + }, + { + name: "Ext8.error.type", + code: def.Ext8, + r: []byte{1}, + err: io.EOF, + count: 1, + }, + { + name: "Ext8.error.data", + code: def.Ext8, + r: []byte{1, 18}, + err: io.EOF, + count: 2, + }, + { + name: "Ext8.ok", + code: def.Ext8, + r: []byte{1, 18, 2}, + typ: 18, + data: []byte{2}, + count: 3, + }, + + { + name: "Ext16.error.size", + code: def.Ext16, + r: []byte{}, + err: io.EOF, + count: 0, + }, + { + name: "Ext16.error.type", + code: def.Ext16, + r: []byte{0, 1}, + err: io.EOF, + count: 1, + }, + { + name: "Ext16.error.data", + code: def.Ext16, + r: []byte{0, 1, 24}, + err: io.EOF, + count: 2, + }, + { + name: "Ext16.ok", + code: def.Ext16, + r: []byte{0, 1, 24, 3}, + typ: 24, + data: []byte{3}, + count: 3, + }, + + { + name: "Ext32.error.size", + code: def.Ext32, + r: []byte{}, + err: io.EOF, + count: 0, + }, + { + name: "Ext32.error.type", + code: def.Ext32, + r: []byte{0, 0, 0, 1}, + err: io.EOF, + count: 1, + }, + { + name: "Ext32.error.data", + code: def.Ext32, + r: []byte{0, 0, 0, 1, 32}, + err: io.EOF, + count: 2, + }, + { + name: "Ext32.ok", + code: def.Ext32, + r: []byte{0, 0, 0, 1, 32, 4}, + typ: 32, + data: []byte{4}, + count: 3, + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + r := tu.NewTestReader(tc.r) + d := decoder{ + r: r, + buf: common.GetBuffer(), + } + defer common.PutBuffer(d.buf) + typ, data, err := d.readIfExtType(tc.code) + tu.IsError(t, err, tc.err) + tu.Equal(t, tc.typ, typ) + tu.EqualSlice(t, data, tc.data) + tu.Equal(t, r.Count(), tc.count) + }) + } +} From 4aea8d68e2181aa7b60b2ee881c89f8d017e6d1d Mon Sep 17 00:00:00 2001 From: shamaton Date: Mon, 5 Aug 2024 23:57:55 +0900 Subject: [PATCH 06/41] add decoding bool and map tests --- internal/common/testutil/testutil.go | 11 + internal/stream/decoding/bool_test.go | 60 ++ internal/stream/decoding/decoding_test.go | 99 ++++ internal/stream/decoding/map_test.go | 654 ++++++++++++++++++++++ 4 files changed, 824 insertions(+) create mode 100644 internal/stream/decoding/bool_test.go create mode 100644 internal/stream/decoding/decoding_test.go create mode 100644 internal/stream/decoding/map_test.go diff --git a/internal/common/testutil/testutil.go b/internal/common/testutil/testutil.go index f1d4405..dccef7a 100644 --- a/internal/common/testutil/testutil.go +++ b/internal/common/testutil/testutil.go @@ -2,6 +2,7 @@ package testutil import ( "errors" + "maps" "strings" "testing" ) @@ -53,3 +54,13 @@ func EqualSlice[T comparable](t *testing.T, actual, expected []T) { Equal[T](t, actual[i], expected[i]) } } + +func EqualMap[K comparable, V comparable](t *testing.T, actual, expected map[K]V) { + t.Helper() + if len(actual) != len(expected) { + t.Fatalf("diffrent length. actual: %v, expected: %v", actual, expected) + } + if !maps.Equal(actual, expected) { + t.Fatalf("not equal. actual: %v, expected: %v", actual, expected) + } +} diff --git a/internal/stream/decoding/bool_test.go b/internal/stream/decoding/bool_test.go new file mode 100644 index 0000000..cb1737a --- /dev/null +++ b/internal/stream/decoding/bool_test.go @@ -0,0 +1,60 @@ +package decoding + +import ( + "reflect" + "testing" + + "github.com/shamaton/msgpack/v2/def" +) + +func Test_asBool(t *testing.T) { + method := func(d *decoder) func(reflect.Kind) (bool, error) { + return d.asBool + } + testcases := AsXXXTestCases[bool]{ + { + Name: "Bool", + Data: []byte{def.True}, + Expected: true, + ReadCount: 1, + MethodAs: method, + }, + } + + for _, tc := range testcases { + tc.Run(t) + } +} + +func Test_asBoolWithCode(t *testing.T) { + method := func(d *decoder) func(byte, reflect.Kind) (bool, error) { + return d.asBoolWithCode + } + testcases := AsXXXTestCases[bool]{ + { + Name: "True", + Code: def.True, + Expected: true, + IsSkipNgCase: true, + MethodAsWithCode: method, + }, + { + Name: "False", + Code: def.False, + Expected: false, + IsSkipNgCase: true, + MethodAsWithCode: method, + }, + { + Name: "Unexpected", + Code: def.Nil, + IsSkipNgCase: true, + IsTemplateError: true, + MethodAsWithCode: method, + }, + } + + for _, tc := range testcases { + tc.Run(t) + } +} diff --git a/internal/stream/decoding/decoding_test.go b/internal/stream/decoding/decoding_test.go new file mode 100644 index 0000000..13b0f79 --- /dev/null +++ b/internal/stream/decoding/decoding_test.go @@ -0,0 +1,99 @@ +package decoding + +import ( + "fmt" + "io" + "reflect" + "testing" + + "github.com/shamaton/msgpack/v2/internal/common" + tu "github.com/shamaton/msgpack/v2/internal/common/testutil" +) + +type AsXXXTestCase[T comparable] struct { + Name string + Code byte + Data []byte + ReadCount int + Length int + Expected T + IsSkipNgCase bool + IsSkipOkCase bool + IsTemplateError bool + MethodAs func(d *decoder) func(reflect.Kind) (T, error) + MethodAsWithCode func(d *decoder) func(byte, reflect.Kind) (T, error) + MethodAsCustom func(d *decoder) (T, error) +} + +type AsXXXTestCases[T comparable] []AsXXXTestCase[T] + +func (tc *AsXXXTestCase[T]) Run(t *testing.T) { + const kind = reflect.String + t.Helper() + + if tc.MethodAs == nil && tc.MethodAsWithCode == nil && tc.MethodAsCustom == nil { + t.Fatal("must set either method or methodAsWithCode or MethodAsCustom") + } + + methodAs := func(d *decoder) (T, error) { + if tc.MethodAs != nil { + return tc.MethodAs(d)(kind) + } + if tc.MethodAsWithCode != nil { + return tc.MethodAsWithCode(d)(tc.Code, kind) + } + if tc.MethodAsCustom != nil { + return tc.MethodAsCustom(d) + } + panic("unreachable") + } + + t.Run(tc.Name, func(t *testing.T) { + t.Run("read error", func(t *testing.T) { + if tc.IsSkipNgCase { + t.Log("this testcase is skipped by skip flag") + return + } + d := decoder{ + r: tu.NewErrReader(), + buf: common.GetBuffer(), + } + defer common.PutBuffer(d.buf) + + _, err := methodAs(&d) + tu.IsError(t, err, tu.ErrReaderErr) + }) + + name := "ok" + if tc.IsTemplateError { + name += " but template error" + } + t.Run(name, func(t *testing.T) { + if tc.IsSkipOkCase { + t.Log("this testcase is skipped by skip flag") + return + } + + r := tu.NewTestReader(tc.Data) + d := decoder{ + r: r, + buf: common.GetBuffer(), + } + defer common.PutBuffer(d.buf) + + v, err := methodAs(&d) + if tc.IsTemplateError { + tu.ErrorContains(t, err, fmt.Sprintf("msgpack : invalid code %x", tc.Code)) + return + } + tu.NoError(t, err) + tu.Equal(t, v, tc.Expected) + tu.Equal(t, r.Count(), tc.ReadCount) + + p := make([]byte, 1) + n, err := d.r.Read(p) + tu.IsError(t, err, io.EOF) + tu.Equal(t, n, 0) + }) + }) +} diff --git a/internal/stream/decoding/map_test.go b/internal/stream/decoding/map_test.go new file mode 100644 index 0000000..c746eba --- /dev/null +++ b/internal/stream/decoding/map_test.go @@ -0,0 +1,654 @@ +package decoding + +import ( + "bytes" + "fmt" + "math" + "reflect" + "testing" + + tu "github.com/shamaton/msgpack/v2/internal/common/testutil" + "github.com/shamaton/msgpack/v2/internal/stream/encoding" + + "github.com/shamaton/msgpack/v2/def" +) + +func Test_mapLength(t *testing.T) { + method := func(d *decoder) func(byte, reflect.Kind) (int, error) { + return d.mapLength + } + testcases := AsXXXTestCases[int]{ + { + Name: "FixMap", + Code: def.FixMap + 3, + Expected: 3, + MethodAsWithCode: method, + IsSkipNgCase: true, + }, + { + Name: "Map16", + Code: def.Map16, + Data: []byte{0xff, 0xff}, + Expected: math.MaxUint16, + ReadCount: 1, + MethodAsWithCode: method, + }, + { + Name: "Map32", + Code: def.Map32, + Data: []byte{0xff, 0xff, 0xff, 0xff}, + Expected: math.MaxUint32, + ReadCount: 1, + MethodAsWithCode: method, + }, + { + Name: "Unexpected", + Code: def.Nil, + IsSkipNgCase: true, + IsTemplateError: true, + MethodAsWithCode: method, + }, + } + + for _, tc := range testcases { + tc.Run(t) + } +} + +func TestHoge(t *testing.T) { + buf := new(bytes.Buffer) + err := encoding.Encode(buf, map[string]int{"a": 1}, false) + tu.NoError(t, err) + + v := map[string]int{} + err = Decode(buf, &v, false) + tu.NoError(t, err) + t.Log(v) +} + +func Test_asFixedMap_StringInt(t *testing.T) { + run := func(t *testing.T, v any, dv byte) { + method := func(d *decoder) (bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedMap(rv.Elem(), 1) + } + + name := fmt.Sprintf("%T", v) + testcases := AsXXXTestCases[bool]{ + { + Name: name + ".error.asString", + Code: def.Int32, + Data: []byte{def.Int32}, + Expected: false, + MethodAsCustom: method, + IsSkipNgCase: true, + IsTemplateError: true, + }, + { + Name: name + ".error.asInt", + Code: def.Str8, + Data: []byte{def.FixStr + 1, 'a', def.Str8}, + Expected: false, + MethodAsCustom: method, + IsSkipNgCase: true, + IsTemplateError: true, + }, + { + Name: name + ".ok", + Data: []byte{def.FixStr + 1, 'a', def.PositiveFixIntMin + dv}, + Expected: true, + ReadCount: 3, + MethodAsCustom: method, + IsSkipNgCase: true, + }, + } + for _, tc := range testcases { + tc.Run(t) + } + } + + v1 := new(map[string]int) + run(t, v1, 1) + tu.EqualMap(t, *v1, map[string]int{"a": 1}) + v2 := new(map[string]int8) + run(t, v2, 2) + tu.EqualMap(t, *v2, map[string]int8{"a": 2}) + v3 := new(map[string]int16) + run(t, v3, 3) + tu.EqualMap(t, *v3, map[string]int16{"a": 3}) + v4 := new(map[string]int32) + run(t, v4, 4) + tu.EqualMap(t, *v4, map[string]int32{"a": 4}) + v5 := new(map[string]int64) + run(t, v5, 5) + tu.EqualMap(t, *v5, map[string]int64{"a": 5}) +} + +func Test_asFixedMap_StringUint(t *testing.T) { + run := func(t *testing.T, v any, dv byte) { + method := func(d *decoder) (bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedMap(rv.Elem(), 1) + } + + name := fmt.Sprintf("%T", v) + testcases := AsXXXTestCases[bool]{ + { + Name: name + ".error.asString", + Code: def.Int32, + Data: []byte{def.Int32}, + Expected: false, + MethodAsCustom: method, + IsSkipNgCase: true, + IsTemplateError: true, + }, + { + Name: name + ".error.asUint", + Code: def.Str8, + Data: []byte{def.FixStr + 1, 'a', def.Str8}, + Expected: false, + MethodAsCustom: method, + IsSkipNgCase: true, + IsTemplateError: true, + }, + { + Name: name + ".ok", + Data: []byte{def.FixStr + 1, 'a', def.Uint8, dv}, + Expected: true, + ReadCount: 4, + MethodAsCustom: method, + IsSkipNgCase: true, + }, + } + for _, tc := range testcases { + tc.Run(t) + } + } + + v1 := new(map[string]uint) + run(t, v1, 1) + tu.EqualMap(t, *v1, map[string]uint{"a": 1}) + v2 := new(map[string]uint8) + run(t, v2, 2) + tu.EqualMap(t, *v2, map[string]uint8{"a": 2}) + v3 := new(map[string]uint16) + run(t, v3, 3) + tu.EqualMap(t, *v3, map[string]uint16{"a": 3}) + v4 := new(map[string]uint32) + run(t, v4, 4) + tu.EqualMap(t, *v4, map[string]uint32{"a": 4}) + v5 := new(map[string]uint64) + run(t, v5, 5) + tu.EqualMap(t, *v5, map[string]uint64{"a": 5}) +} + +func Test_asFixedMap_StringFloat(t *testing.T) { + run := func(t *testing.T, v any, dv byte) { + method := func(d *decoder) (bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedMap(rv.Elem(), 1) + } + + name := fmt.Sprintf("%T", v) + testcases := AsXXXTestCases[bool]{ + { + Name: name + ".error.asString", + Code: def.Int32, + Data: []byte{def.Int32}, + Expected: false, + MethodAsCustom: method, + IsSkipNgCase: true, + IsTemplateError: true, + }, + { + Name: name + ".error.asFloat", + Code: def.Str8, + Data: []byte{def.FixStr + 1, 'a', def.Str8}, + Expected: false, + MethodAsCustom: method, + IsSkipNgCase: true, + IsTemplateError: true, + }, + { + Name: name + ".ok", + Data: []byte{def.FixStr + 1, 'a', def.Int16, 0, dv}, + Expected: true, + ReadCount: 4, + MethodAsCustom: method, + IsSkipNgCase: true, + }, + } + for _, tc := range testcases { + tc.Run(t) + } + } + + v1 := new(map[string]float32) + run(t, v1, 1) + tu.EqualMap(t, *v1, map[string]float32{"a": 1}) + v2 := new(map[string]float64) + run(t, v2, 2) + tu.EqualMap(t, *v2, map[string]float64{"a": 2}) +} + +func Test_asFixedMap_StringBool(t *testing.T) { + run := func(t *testing.T, v any, dv byte) { + method := func(d *decoder) (bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedMap(rv.Elem(), 1) + } + + name := fmt.Sprintf("%T", v) + testcases := AsXXXTestCases[bool]{ + { + Name: name + ".error.asString", + Code: def.Int32, + Data: []byte{def.Int32}, + Expected: false, + MethodAsCustom: method, + IsSkipNgCase: true, + IsTemplateError: true, + }, + { + Name: name + ".error.asBool", + Code: def.Str8, + Data: []byte{def.FixStr + 1, 'a', def.Str8}, + Expected: false, + MethodAsCustom: method, + IsSkipNgCase: true, + IsTemplateError: true, + }, + { + Name: name + ".ok", + Data: []byte{def.FixStr + 1, 'a', dv}, + Expected: true, + ReadCount: 3, + MethodAsCustom: method, + IsSkipNgCase: true, + }, + } + for _, tc := range testcases { + tc.Run(t) + } + } + + v1 := new(map[string]bool) + run(t, v1, def.True) + tu.EqualMap(t, *v1, map[string]bool{"a": true}) +} + +func Test_asFixedMap_StringString(t *testing.T) { + run := func(t *testing.T, v any, dv byte) { + method := func(d *decoder) (bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedMap(rv.Elem(), 1) + } + + name := fmt.Sprintf("%T", v) + testcases := AsXXXTestCases[bool]{ + { + Name: name + ".error.asString", + Code: def.Int32, + Data: []byte{def.Int32}, + Expected: false, + MethodAsCustom: method, + IsSkipNgCase: true, + IsTemplateError: true, + }, + { + Name: name + ".error.asString", + Code: def.Int32, + Data: []byte{def.FixStr + 1, 'a', def.Int32}, + Expected: false, + MethodAsCustom: method, + IsSkipNgCase: true, + IsTemplateError: true, + }, + { + Name: name + ".ok", + Data: []byte{def.FixStr + 1, 'a', def.FixStr + 1, dv}, + Expected: true, + ReadCount: 4, + MethodAsCustom: method, + IsSkipNgCase: true, + }, + } + for _, tc := range testcases { + tc.Run(t) + } + } + + v1 := new(map[string]string) + run(t, v1, 'b') + tu.EqualMap(t, *v1, map[string]string{"a": "b"}) +} + +func Test_asFixedMap_IntString(t *testing.T) { + run := func(t *testing.T, v any, dv byte) { + method := func(d *decoder) (bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedMap(rv.Elem(), 1) + } + + name := fmt.Sprintf("%T", v) + testcases := AsXXXTestCases[bool]{ + { + Name: name + ".error.asInt", + Code: def.Str8, + Data: []byte{def.Str8}, + Expected: false, + MethodAsCustom: method, + IsSkipNgCase: true, + IsTemplateError: true, + }, + { + Name: name + ".error.asString", + Code: def.Int32, + Data: []byte{def.Int8, dv, def.Int32}, + Expected: false, + MethodAsCustom: method, + IsSkipNgCase: true, + IsTemplateError: true, + }, + { + Name: name + ".ok", + Data: []byte{def.Int8, dv, def.FixStr + 1, 'b'}, + Expected: true, + ReadCount: 4, + MethodAsCustom: method, + IsSkipNgCase: true, + }, + } + for _, tc := range testcases { + tc.Run(t) + } + } + + v1 := new(map[int]string) + run(t, v1, 1) + tu.EqualMap(t, *v1, map[int]string{1: "b"}) + v2 := new(map[int8]string) + run(t, v2, 2) + tu.EqualMap(t, *v2, map[int8]string{int8(2): "b"}) + v3 := new(map[int16]string) + run(t, v3, 3) + tu.EqualMap(t, *v3, map[int16]string{int16(3): "b"}) + v4 := new(map[int32]string) + run(t, v4, 4) + tu.EqualMap(t, *v4, map[int32]string{int32(4): "b"}) + v5 := new(map[int64]string) + run(t, v5, 5) + tu.EqualMap(t, *v5, map[int64]string{int64(5): "b"}) +} + +func Test_asFixedMap_IntBool(t *testing.T) { + run := func(t *testing.T, v any, dv byte) { + method := func(d *decoder) (bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedMap(rv.Elem(), 1) + } + + name := fmt.Sprintf("%T", v) + testcases := AsXXXTestCases[bool]{ + { + Name: name + ".error.asInt", + Code: def.Str8, + Data: []byte{def.Str8}, + Expected: false, + MethodAsCustom: method, + IsSkipNgCase: true, + IsTemplateError: true, + }, + { + Name: name + ".error.asBool", + Code: def.Int32, + Data: []byte{def.Int8, dv, def.Int32}, + Expected: false, + MethodAsCustom: method, + IsSkipNgCase: true, + IsTemplateError: true, + }, + { + Name: name + ".ok", + Data: []byte{def.Int8, dv, def.True}, + Expected: true, + ReadCount: 3, + MethodAsCustom: method, + IsSkipNgCase: true, + }, + } + for _, tc := range testcases { + tc.Run(t) + } + } + + v1 := new(map[int]bool) + run(t, v1, 1) + tu.EqualMap(t, *v1, map[int]bool{1: true}) + v2 := new(map[int8]bool) + run(t, v2, 2) + tu.EqualMap(t, *v2, map[int8]bool{int8(2): true}) + v3 := new(map[int16]bool) + run(t, v3, 3) + tu.EqualMap(t, *v3, map[int16]bool{int16(3): true}) + v4 := new(map[int32]bool) + run(t, v4, 4) + tu.EqualMap(t, *v4, map[int32]bool{int32(4): true}) + v5 := new(map[int64]bool) + run(t, v5, 5) + tu.EqualMap(t, *v5, map[int64]bool{int64(5): true}) +} + +func Test_asFixedMap_UintString(t *testing.T) { + run := func(t *testing.T, v any, dv byte) { + method := func(d *decoder) (bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedMap(rv.Elem(), 1) + } + + name := fmt.Sprintf("%T", v) + testcases := AsXXXTestCases[bool]{ + { + Name: name + ".error.asUint", + Code: def.Str8, + Data: []byte{def.Str8}, + Expected: false, + MethodAsCustom: method, + IsSkipNgCase: true, + IsTemplateError: true, + }, + { + Name: name + ".error.asString", + Code: def.Int32, + Data: []byte{def.Uint8, dv, def.Int32}, + Expected: false, + MethodAsCustom: method, + IsSkipNgCase: true, + IsTemplateError: true, + }, + { + Name: name + ".ok", + Data: []byte{def.Uint8, dv, def.FixStr + 1, 'b'}, + Expected: true, + ReadCount: 4, + MethodAsCustom: method, + IsSkipNgCase: true, + }, + } + for _, tc := range testcases { + tc.Run(t) + } + } + + v1 := new(map[uint]string) + run(t, v1, 1) + tu.EqualMap(t, *v1, map[uint]string{1: "b"}) + v2 := new(map[uint8]string) + run(t, v2, 2) + tu.EqualMap(t, *v2, map[uint8]string{uint8(2): "b"}) + v3 := new(map[uint16]string) + run(t, v3, 3) + tu.EqualMap(t, *v3, map[uint16]string{uint16(3): "b"}) + v4 := new(map[uint32]string) + run(t, v4, 4) + tu.EqualMap(t, *v4, map[uint32]string{uint32(4): "b"}) + v5 := new(map[uint64]string) + run(t, v5, 5) + tu.EqualMap(t, *v5, map[uint64]string{uint64(5): "b"}) +} + +func Test_asFixedMap_UintBool(t *testing.T) { + run := func(t *testing.T, v any, dv byte) { + method := func(d *decoder) (bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedMap(rv.Elem(), 1) + } + + name := fmt.Sprintf("%T", v) + testcases := AsXXXTestCases[bool]{ + { + Name: name + ".error.asUint", + Code: def.Str8, + Data: []byte{def.Str8}, + Expected: false, + MethodAsCustom: method, + IsSkipNgCase: true, + IsTemplateError: true, + }, + { + Name: name + ".error.asBool", + Code: def.Int32, + Data: []byte{def.Uint8, dv, def.Int32}, + Expected: false, + MethodAsCustom: method, + IsSkipNgCase: true, + IsTemplateError: true, + }, + { + Name: name + ".ok", + Data: []byte{def.Uint8, dv, def.True}, + Expected: true, + ReadCount: 3, + MethodAsCustom: method, + IsSkipNgCase: true, + }, + } + for _, tc := range testcases { + tc.Run(t) + } + } + + v1 := new(map[uint]bool) + run(t, v1, 1) + tu.EqualMap(t, *v1, map[uint]bool{1: true}) + v2 := new(map[uint8]bool) + run(t, v2, 2) + tu.EqualMap(t, *v2, map[uint8]bool{uint8(2): true}) + v3 := new(map[uint16]bool) + run(t, v3, 3) + tu.EqualMap(t, *v3, map[uint16]bool{uint16(3): true}) + v4 := new(map[uint32]bool) + run(t, v4, 4) + tu.EqualMap(t, *v4, map[uint32]bool{uint32(4): true}) + v5 := new(map[uint64]bool) + run(t, v5, 5) + tu.EqualMap(t, *v5, map[uint64]bool{uint64(5): true}) +} + +func Test_asFixedMap_FloatString(t *testing.T) { + run := func(t *testing.T, v any, dv byte) { + method := func(d *decoder) (bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedMap(rv.Elem(), 1) + } + + name := fmt.Sprintf("%T", v) + testcases := AsXXXTestCases[bool]{ + { + Name: name + ".error.asFloat", + Code: def.Str8, + Data: []byte{def.Str8}, + Expected: false, + MethodAsCustom: method, + IsSkipNgCase: true, + IsTemplateError: true, + }, + { + Name: name + ".error.asString", + Code: def.Int32, + Data: []byte{def.Uint8, dv, def.Int32}, + Expected: false, + MethodAsCustom: method, + IsSkipNgCase: true, + IsTemplateError: true, + }, + { + Name: name + ".ok", + Data: []byte{def.Uint8, dv, def.FixStr + 1, 'b'}, + Expected: true, + ReadCount: 4, + MethodAsCustom: method, + IsSkipNgCase: true, + }, + } + for _, tc := range testcases { + tc.Run(t) + } + } + + v1 := new(map[float32]string) + run(t, v1, 1) + tu.EqualMap(t, *v1, map[float32]string{1: "b"}) + v2 := new(map[float64]string) + run(t, v2, 2) + tu.EqualMap(t, *v2, map[float64]string{2: "b"}) +} + +func Test_asFixedMap_FloatBool(t *testing.T) { + run := func(t *testing.T, v any, dv byte) { + method := func(d *decoder) (bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedMap(rv.Elem(), 1) + } + + name := fmt.Sprintf("%T", v) + testcases := AsXXXTestCases[bool]{ + { + Name: name + ".error.asFloat", + Code: def.Str8, + Data: []byte{def.Str8}, + Expected: false, + MethodAsCustom: method, + IsSkipNgCase: true, + IsTemplateError: true, + }, + { + Name: name + ".error.asBool", + Code: def.Int32, + Data: []byte{def.Uint8, dv, def.Int32}, + Expected: false, + MethodAsCustom: method, + IsSkipNgCase: true, + IsTemplateError: true, + }, + { + Name: name + ".ok", + Data: []byte{def.Uint8, dv, def.True}, + Expected: true, + ReadCount: 3, + MethodAsCustom: method, + IsSkipNgCase: true, + }, + } + for _, tc := range testcases { + tc.Run(t) + } + } + + v1 := new(map[float32]bool) + run(t, v1, 1) + tu.EqualMap(t, *v1, map[float32]bool{1: true}) + v2 := new(map[float64]bool) + run(t, v2, 2) + tu.EqualMap(t, *v2, map[float64]bool{2: true}) +} From f5a9a1ae4e09fd8ee87272f7796304366208404e Mon Sep 17 00:00:00 2001 From: shamaton Date: Tue, 6 Aug 2024 18:08:19 +0900 Subject: [PATCH 07/41] add decoding complex tests --- internal/common/testutil/testutil.go | 7 +- internal/stream/decoding/bool_test.go | 3 - internal/stream/decoding/complex.go | 6 +- internal/stream/decoding/complex_test.go | 165 ++++++++++++++++++++++ internal/stream/decoding/decoding_test.go | 65 +++------ internal/stream/decoding/map_test.go | 57 ++------ internal/stream/decoding/string_test.go | 133 +++++++---------- msgpack_test.go | 3 +- 8 files changed, 260 insertions(+), 179 deletions(-) create mode 100644 internal/stream/decoding/complex_test.go diff --git a/internal/common/testutil/testutil.go b/internal/common/testutil/testutil.go index dccef7a..d38d0e5 100644 --- a/internal/common/testutil/testutil.go +++ b/internal/common/testutil/testutil.go @@ -2,7 +2,6 @@ package testutil import ( "errors" - "maps" "strings" "testing" ) @@ -60,7 +59,9 @@ func EqualMap[K comparable, V comparable](t *testing.T, actual, expected map[K]V if len(actual) != len(expected) { t.Fatalf("diffrent length. actual: %v, expected: %v", actual, expected) } - if !maps.Equal(actual, expected) { - t.Fatalf("not equal. actual: %v, expected: %v", actual, expected) + for k, v1 := range actual { + if v2, ok := expected[k]; !ok || v1 != v2 { + t.Fatalf("not equal. actual: %v, expected: %v", actual, expected) + } } } diff --git a/internal/stream/decoding/bool_test.go b/internal/stream/decoding/bool_test.go index cb1737a..186e99a 100644 --- a/internal/stream/decoding/bool_test.go +++ b/internal/stream/decoding/bool_test.go @@ -35,20 +35,17 @@ func Test_asBoolWithCode(t *testing.T) { Name: "True", Code: def.True, Expected: true, - IsSkipNgCase: true, MethodAsWithCode: method, }, { Name: "False", Code: def.False, Expected: false, - IsSkipNgCase: true, MethodAsWithCode: method, }, { Name: "Unexpected", Code: def.Nil, - IsSkipNgCase: true, IsTemplateError: true, MethodAsWithCode: method, }, diff --git a/internal/stream/decoding/complex.go b/internal/stream/decoding/complex.go index 79eea11..07471d2 100644 --- a/internal/stream/decoding/complex.go +++ b/internal/stream/decoding/complex.go @@ -55,7 +55,7 @@ func (d *decoder) asComplex64(code byte, k reflect.Kind) (complex64, error) { } - return complex(0, 0), fmt.Errorf("should not reach this line!! code %x decoding %v", code, k) + return complex(0, 0), d.errorTemplate(code, k) } func (d *decoder) asComplex128(code byte, k reflect.Kind) (complex128, error) { @@ -94,7 +94,7 @@ func (d *decoder) asComplex128(code byte, k reflect.Kind) (complex128, error) { return complex(0, 0), err } r := math.Float64frombits(binary.BigEndian.Uint64(rb)) - + ib, err := d.readSize8() if err != nil { return complex(0, 0), err @@ -104,5 +104,5 @@ func (d *decoder) asComplex128(code byte, k reflect.Kind) (complex128, error) { } - return complex(0, 0), fmt.Errorf("should not reach this line!! code %x decoding %v", code, k) + return complex(0, 0), d.errorTemplate(code, k) } diff --git a/internal/stream/decoding/complex_test.go b/internal/stream/decoding/complex_test.go new file mode 100644 index 0000000..79317bd --- /dev/null +++ b/internal/stream/decoding/complex_test.go @@ -0,0 +1,165 @@ +package decoding + +import ( + "io" + "reflect" + "testing" + + "github.com/shamaton/msgpack/v2/def" +) + +func Test_asComplex64(t *testing.T) { + method := func(d *decoder) func(byte, reflect.Kind) (complex64, error) { + return d.asComplex64 + } + testcases := AsXXXTestCases[complex64]{ + { + Name: "Fixext8.error.type", + Code: def.Fixext8, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Fixext8.error.r", + Code: def.Fixext8, + Data: []byte{byte(def.ComplexTypeCode())}, + Error: io.EOF, + ReadCount: 1, + MethodAsWithCode: method, + }, + { + Name: "Fixext8.error.i", + Code: def.Fixext8, + Data: []byte{byte(def.ComplexTypeCode()), 0, 0, 0, 1}, + Error: io.EOF, + ReadCount: 2, + MethodAsWithCode: method, + }, + { + Name: "Fixext8.ok", + Code: def.Fixext8, + Data: []byte{byte(def.ComplexTypeCode()), 63, 128, 0, 0, 63, 128, 0, 0}, + Expected: complex(1, 1), + ReadCount: 3, + MethodAsWithCode: method, + }, + { + Name: "Fixext16.error.type", + Code: def.Fixext16, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Fixext16.error.r", + Code: def.Fixext16, + Data: []byte{byte(def.ComplexTypeCode())}, + Error: io.EOF, + ReadCount: 1, + MethodAsWithCode: method, + }, + { + Name: "Fixext16.error.i", + Code: def.Fixext16, + Data: []byte{byte(def.ComplexTypeCode()), 0, 0, 0, 1}, + Error: io.EOF, + ReadCount: 2, + MethodAsWithCode: method, + }, + { + Name: "Fixext16.ok", + Code: def.Fixext16, + Data: []byte{byte(def.ComplexTypeCode()), + 63, 240, 0, 0, 0, 0, 0, 0, 63, 240, 0, 0, 0, 0, 0, 0}, + Expected: complex(1, 1), + ReadCount: 3, + MethodAsWithCode: method, + }, + { + Name: "Unexpected", + Code: def.Nil, + IsTemplateError: true, + MethodAsWithCode: method, + }, + } + for _, tc := range testcases { + tc.Run(t) + } +} + +func Test_asComplex128(t *testing.T) { + method := func(d *decoder) func(byte, reflect.Kind) (complex128, error) { + return d.asComplex128 + } + testcases := AsXXXTestCases[complex128]{ + { + Name: "Fixext8.error.type", + Code: def.Fixext8, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Fixext8.error.r", + Code: def.Fixext8, + Data: []byte{byte(def.ComplexTypeCode())}, + Error: io.EOF, + ReadCount: 1, + MethodAsWithCode: method, + }, + { + Name: "Fixext8.error.i", + Code: def.Fixext8, + Data: []byte{byte(def.ComplexTypeCode()), 0, 0, 0, 1}, + Error: io.EOF, + ReadCount: 2, + MethodAsWithCode: method, + }, + { + Name: "Fixext8.ok", + Code: def.Fixext8, + Data: []byte{byte(def.ComplexTypeCode()), 63, 128, 0, 0, 63, 128, 0, 0}, + Expected: complex(1, 1), + ReadCount: 3, + MethodAsWithCode: method, + }, + { + Name: "Fixext16.error.type", + Code: def.Fixext16, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Fixext16.error.r", + Code: def.Fixext16, + Data: []byte{byte(def.ComplexTypeCode())}, + Error: io.EOF, + ReadCount: 1, + MethodAsWithCode: method, + }, + { + Name: "Fixext16.error.i", + Code: def.Fixext16, + Data: []byte{byte(def.ComplexTypeCode()), 0, 0, 0, 1}, + Error: io.EOF, + ReadCount: 2, + MethodAsWithCode: method, + }, + { + Name: "Fixext16.ok", + Code: def.Fixext16, + Data: []byte{byte(def.ComplexTypeCode()), + 63, 240, 0, 0, 0, 0, 0, 0, 63, 240, 0, 0, 0, 0, 0, 0}, + Expected: complex(1, 1), + ReadCount: 3, + MethodAsWithCode: method, + }, + { + Name: "Unexpected", + Code: def.Nil, + IsTemplateError: true, + MethodAsWithCode: method, + }, + } + for _, tc := range testcases { + tc.Run(t) + } +} diff --git a/internal/stream/decoding/decoding_test.go b/internal/stream/decoding/decoding_test.go index 13b0f79..0ea5c9b 100644 --- a/internal/stream/decoding/decoding_test.go +++ b/internal/stream/decoding/decoding_test.go @@ -15,10 +15,8 @@ type AsXXXTestCase[T comparable] struct { Code byte Data []byte ReadCount int - Length int Expected T - IsSkipNgCase bool - IsSkipOkCase bool + Error error IsTemplateError bool MethodAs func(d *decoder) func(reflect.Kind) (T, error) MethodAsWithCode func(d *decoder) func(byte, reflect.Kind) (T, error) @@ -49,51 +47,30 @@ func (tc *AsXXXTestCase[T]) Run(t *testing.T) { } t.Run(tc.Name, func(t *testing.T) { - t.Run("read error", func(t *testing.T) { - if tc.IsSkipNgCase { - t.Log("this testcase is skipped by skip flag") - return - } - d := decoder{ - r: tu.NewErrReader(), - buf: common.GetBuffer(), - } - defer common.PutBuffer(d.buf) + r := tu.NewTestReader(tc.Data) + d := decoder{ + r: r, + buf: common.GetBuffer(), + } + defer common.PutBuffer(d.buf) - _, err := methodAs(&d) - tu.IsError(t, err, tu.ErrReaderErr) - }) + v, err := methodAs(&d) + tu.Equal(t, r.Count(), tc.ReadCount) - name := "ok" + if tc.Error != nil { + tu.IsError(t, err, tc.Error) + return + } if tc.IsTemplateError { - name += " but template error" + tu.ErrorContains(t, err, fmt.Sprintf("msgpack : invalid code %x", tc.Code)) + return } - t.Run(name, func(t *testing.T) { - if tc.IsSkipOkCase { - t.Log("this testcase is skipped by skip flag") - return - } - - r := tu.NewTestReader(tc.Data) - d := decoder{ - r: r, - buf: common.GetBuffer(), - } - defer common.PutBuffer(d.buf) - - v, err := methodAs(&d) - if tc.IsTemplateError { - tu.ErrorContains(t, err, fmt.Sprintf("msgpack : invalid code %x", tc.Code)) - return - } - tu.NoError(t, err) - tu.Equal(t, v, tc.Expected) - tu.Equal(t, r.Count(), tc.ReadCount) + tu.NoError(t, err) + tu.Equal(t, v, tc.Expected) - p := make([]byte, 1) - n, err := d.r.Read(p) - tu.IsError(t, err, io.EOF) - tu.Equal(t, n, 0) - }) + p := make([]byte, 1) + n, err := d.r.Read(p) + tu.IsError(t, err, io.EOF) + tu.Equal(t, n, 0) }) } diff --git a/internal/stream/decoding/map_test.go b/internal/stream/decoding/map_test.go index c746eba..d327974 100644 --- a/internal/stream/decoding/map_test.go +++ b/internal/stream/decoding/map_test.go @@ -23,7 +23,6 @@ func Test_mapLength(t *testing.T) { Code: def.FixMap + 3, Expected: 3, MethodAsWithCode: method, - IsSkipNgCase: true, }, { Name: "Map16", @@ -44,7 +43,6 @@ func Test_mapLength(t *testing.T) { { Name: "Unexpected", Code: def.Nil, - IsSkipNgCase: true, IsTemplateError: true, MethodAsWithCode: method, }, @@ -81,7 +79,6 @@ func Test_asFixedMap_StringInt(t *testing.T) { Data: []byte{def.Int32}, Expected: false, MethodAsCustom: method, - IsSkipNgCase: true, IsTemplateError: true, }, { @@ -90,7 +87,6 @@ func Test_asFixedMap_StringInt(t *testing.T) { Data: []byte{def.FixStr + 1, 'a', def.Str8}, Expected: false, MethodAsCustom: method, - IsSkipNgCase: true, IsTemplateError: true, }, { @@ -99,7 +95,6 @@ func Test_asFixedMap_StringInt(t *testing.T) { Expected: true, ReadCount: 3, MethodAsCustom: method, - IsSkipNgCase: true, }, } for _, tc := range testcases { @@ -139,7 +134,6 @@ func Test_asFixedMap_StringUint(t *testing.T) { Data: []byte{def.Int32}, Expected: false, MethodAsCustom: method, - IsSkipNgCase: true, IsTemplateError: true, }, { @@ -148,7 +142,6 @@ func Test_asFixedMap_StringUint(t *testing.T) { Data: []byte{def.FixStr + 1, 'a', def.Str8}, Expected: false, MethodAsCustom: method, - IsSkipNgCase: true, IsTemplateError: true, }, { @@ -157,7 +150,6 @@ func Test_asFixedMap_StringUint(t *testing.T) { Expected: true, ReadCount: 4, MethodAsCustom: method, - IsSkipNgCase: true, }, } for _, tc := range testcases { @@ -197,7 +189,6 @@ func Test_asFixedMap_StringFloat(t *testing.T) { Data: []byte{def.Int32}, Expected: false, MethodAsCustom: method, - IsSkipNgCase: true, IsTemplateError: true, }, { @@ -206,7 +197,6 @@ func Test_asFixedMap_StringFloat(t *testing.T) { Data: []byte{def.FixStr + 1, 'a', def.Str8}, Expected: false, MethodAsCustom: method, - IsSkipNgCase: true, IsTemplateError: true, }, { @@ -215,7 +205,6 @@ func Test_asFixedMap_StringFloat(t *testing.T) { Expected: true, ReadCount: 4, MethodAsCustom: method, - IsSkipNgCase: true, }, } for _, tc := range testcases { @@ -246,7 +235,6 @@ func Test_asFixedMap_StringBool(t *testing.T) { Data: []byte{def.Int32}, Expected: false, MethodAsCustom: method, - IsSkipNgCase: true, IsTemplateError: true, }, { @@ -255,7 +243,6 @@ func Test_asFixedMap_StringBool(t *testing.T) { Data: []byte{def.FixStr + 1, 'a', def.Str8}, Expected: false, MethodAsCustom: method, - IsSkipNgCase: true, IsTemplateError: true, }, { @@ -264,7 +251,6 @@ func Test_asFixedMap_StringBool(t *testing.T) { Expected: true, ReadCount: 3, MethodAsCustom: method, - IsSkipNgCase: true, }, } for _, tc := range testcases { @@ -292,7 +278,6 @@ func Test_asFixedMap_StringString(t *testing.T) { Data: []byte{def.Int32}, Expected: false, MethodAsCustom: method, - IsSkipNgCase: true, IsTemplateError: true, }, { @@ -301,7 +286,6 @@ func Test_asFixedMap_StringString(t *testing.T) { Data: []byte{def.FixStr + 1, 'a', def.Int32}, Expected: false, MethodAsCustom: method, - IsSkipNgCase: true, IsTemplateError: true, }, { @@ -310,7 +294,6 @@ func Test_asFixedMap_StringString(t *testing.T) { Expected: true, ReadCount: 4, MethodAsCustom: method, - IsSkipNgCase: true, }, } for _, tc := range testcases { @@ -338,7 +321,6 @@ func Test_asFixedMap_IntString(t *testing.T) { Data: []byte{def.Str8}, Expected: false, MethodAsCustom: method, - IsSkipNgCase: true, IsTemplateError: true, }, { @@ -347,7 +329,6 @@ func Test_asFixedMap_IntString(t *testing.T) { Data: []byte{def.Int8, dv, def.Int32}, Expected: false, MethodAsCustom: method, - IsSkipNgCase: true, IsTemplateError: true, }, { @@ -356,7 +337,6 @@ func Test_asFixedMap_IntString(t *testing.T) { Expected: true, ReadCount: 4, MethodAsCustom: method, - IsSkipNgCase: true, }, } for _, tc := range testcases { @@ -396,7 +376,6 @@ func Test_asFixedMap_IntBool(t *testing.T) { Data: []byte{def.Str8}, Expected: false, MethodAsCustom: method, - IsSkipNgCase: true, IsTemplateError: true, }, { @@ -405,7 +384,6 @@ func Test_asFixedMap_IntBool(t *testing.T) { Data: []byte{def.Int8, dv, def.Int32}, Expected: false, MethodAsCustom: method, - IsSkipNgCase: true, IsTemplateError: true, }, { @@ -414,7 +392,6 @@ func Test_asFixedMap_IntBool(t *testing.T) { Expected: true, ReadCount: 3, MethodAsCustom: method, - IsSkipNgCase: true, }, } for _, tc := range testcases { @@ -454,7 +431,6 @@ func Test_asFixedMap_UintString(t *testing.T) { Data: []byte{def.Str8}, Expected: false, MethodAsCustom: method, - IsSkipNgCase: true, IsTemplateError: true, }, { @@ -463,7 +439,6 @@ func Test_asFixedMap_UintString(t *testing.T) { Data: []byte{def.Uint8, dv, def.Int32}, Expected: false, MethodAsCustom: method, - IsSkipNgCase: true, IsTemplateError: true, }, { @@ -472,7 +447,6 @@ func Test_asFixedMap_UintString(t *testing.T) { Expected: true, ReadCount: 4, MethodAsCustom: method, - IsSkipNgCase: true, }, } for _, tc := range testcases { @@ -512,7 +486,6 @@ func Test_asFixedMap_UintBool(t *testing.T) { Data: []byte{def.Str8}, Expected: false, MethodAsCustom: method, - IsSkipNgCase: true, IsTemplateError: true, }, { @@ -521,7 +494,6 @@ func Test_asFixedMap_UintBool(t *testing.T) { Data: []byte{def.Uint8, dv, def.Int32}, Expected: false, MethodAsCustom: method, - IsSkipNgCase: true, IsTemplateError: true, }, { @@ -530,7 +502,6 @@ func Test_asFixedMap_UintBool(t *testing.T) { Expected: true, ReadCount: 3, MethodAsCustom: method, - IsSkipNgCase: true, }, } for _, tc := range testcases { @@ -570,7 +541,6 @@ func Test_asFixedMap_FloatString(t *testing.T) { Data: []byte{def.Str8}, Expected: false, MethodAsCustom: method, - IsSkipNgCase: true, IsTemplateError: true, }, { @@ -579,7 +549,6 @@ func Test_asFixedMap_FloatString(t *testing.T) { Data: []byte{def.Uint8, dv, def.Int32}, Expected: false, MethodAsCustom: method, - IsSkipNgCase: true, IsTemplateError: true, }, { @@ -588,7 +557,6 @@ func Test_asFixedMap_FloatString(t *testing.T) { Expected: true, ReadCount: 4, MethodAsCustom: method, - IsSkipNgCase: true, }, } for _, tc := range testcases { @@ -614,21 +582,21 @@ func Test_asFixedMap_FloatBool(t *testing.T) { name := fmt.Sprintf("%T", v) testcases := AsXXXTestCases[bool]{ { - Name: name + ".error.asFloat", - Code: def.Str8, - Data: []byte{def.Str8}, - Expected: false, - MethodAsCustom: method, - IsSkipNgCase: true, + Name: name + ".error.asFloat", + Code: def.Str8, + Data: []byte{def.Str8}, + Expected: false, + MethodAsCustom: method, + IsTemplateError: true, }, { - Name: name + ".error.asBool", - Code: def.Int32, - Data: []byte{def.Uint8, dv, def.Int32}, - Expected: false, - MethodAsCustom: method, - IsSkipNgCase: true, + Name: name + ".error.asBool", + Code: def.Int32, + Data: []byte{def.Uint8, dv, def.Int32}, + Expected: false, + MethodAsCustom: method, + IsTemplateError: true, }, { @@ -637,7 +605,6 @@ func Test_asFixedMap_FloatBool(t *testing.T) { Expected: true, ReadCount: 3, MethodAsCustom: method, - IsSkipNgCase: true, }, } for _, tc := range testcases { diff --git a/internal/stream/decoding/string_test.go b/internal/stream/decoding/string_test.go index 6c3ea6e..df941e2 100644 --- a/internal/stream/decoding/string_test.go +++ b/internal/stream/decoding/string_test.go @@ -13,103 +13,76 @@ import ( ) func Test_stringByteLength(t *testing.T) { - testcases := []struct { - name string - code byte - length int - expected int - errSkip bool - }{ + method := func(d *decoder) func(byte, reflect.Kind) (int, error) { + return d.stringByteLength + } + testcases := AsXXXTestCases[int]{ { - name: "FixStr", - code: def.FixStr + 1, - expected: 1, - errSkip: true, + Name: "FixStr", + Code: def.FixStr + 1, + Expected: 1, + MethodAsWithCode: method, }, { - name: "Str8", - code: def.Str8, - length: 1, - expected: math.MaxUint8, + Name: "Str8", + Code: def.Str8, + Data: []byte{0xff}, + Expected: math.MaxUint8, + ReadCount: 1, + MethodAsWithCode: method, }, { - name: "Str16", - code: def.Str16, - length: 2, - expected: math.MaxUint16, + Name: "Str16", + Code: def.Str16, + Data: []byte{0xff, 0xff}, + Expected: math.MaxUint16, + ReadCount: 1, + MethodAsWithCode: method, }, { - name: "Str32", - code: def.Str32, - length: 4, - expected: math.MaxUint32, + Name: "Str32", + Code: def.Str32, + Data: []byte{0xff, 0xff, 0xff, 0xff}, + Expected: math.MaxUint32, + ReadCount: 1, + MethodAsWithCode: method, }, { - name: "Nil", - code: def.Nil, - expected: 0, - errSkip: true, + Name: "Nil", + Code: def.Nil, + Expected: 0, + MethodAsWithCode: method, }, } for _, tc := range testcases { - t.Run(tc.name+"", func(t *testing.T) { - t.Run("ng", func(t *testing.T) { - if tc.errSkip { - t.Log("this testcase is skipped by skip flag") - return - } - d := decoder{ - r: tu.NewErrReader(), - buf: common.GetBuffer(), - } - defer common.PutBuffer(d.buf) - _, err := d.stringByteLength(tc.code, reflect.String) - tu.IsError(t, err, tu.ErrReaderErr) - }) - t.Run("ok", func(t *testing.T) { - data := make([]byte, tc.length) - for i := range data { - data[i] = 0xff - } - - d := decoder{ - r: bytes.NewReader(data), - buf: common.GetBuffer(), - } - defer common.PutBuffer(d.buf) - v, err := d.stringByteLength(tc.code, reflect.String) - tu.NoError(t, err) - tu.Equal(t, v, tc.expected) - - p := make([]byte, 1) - n, err := d.r.Read(p) - tu.IsError(t, err, io.EOF) - tu.Equal(t, n, 0) - }) - }) + tc.Run(t) } } func Test_asString(t *testing.T) { - t.Run("read error", func(t *testing.T) { - d := decoder{ - r: tu.NewErrReader(), - buf: common.GetBuffer(), - } - v, err := d.asString(reflect.String) - tu.IsError(t, err, tu.ErrReaderErr) - tu.Equal(t, v, emptyString) - }) - t.Run("ok", func(t *testing.T) { - d := decoder{ - r: bytes.NewReader([]byte{def.FixStr + 1, 'a'}), - buf: common.GetBuffer(), - } - v, err := d.asString(reflect.String) - tu.NoError(t, err) - tu.Equal(t, v, "a") - }) + method := func(d *decoder) func(reflect.Kind) (string, error) { + return d.asString + } + testcases := AsXXXTestCases[string]{ + { + Name: "String.error", + Data: []byte{def.FixStr + 1}, + Error: io.EOF, + MethodAs: method, + }, + { + Name: "String.ok", + Data: []byte{def.FixStr + 1, 'a'}, + Expected: "a", + ReadCount: 2, + MethodAs: method, + }, + } + + for _, tc := range testcases { + tc.Run(t) + } } func Test_asStringByte(t *testing.T) { diff --git a/msgpack_test.go b/msgpack_test.go index 4703000..7a4cb48 100644 --- a/msgpack_test.go +++ b/msgpack_test.go @@ -6,7 +6,6 @@ import ( "encoding/hex" "errors" "fmt" - "github.com/shamaton/msgpack/v2/internal/common" "io" "math" "math/rand" @@ -15,6 +14,8 @@ import ( "testing" "time" + "github.com/shamaton/msgpack/v2/internal/common" + "github.com/shamaton/msgpack/v2" "github.com/shamaton/msgpack/v2/def" "github.com/shamaton/msgpack/v2/ext" From f70d95fa7f8d0e6944425955d8f76159b7571487 Mon Sep 17 00:00:00 2001 From: shamaton Date: Tue, 6 Aug 2024 18:14:18 +0900 Subject: [PATCH 08/41] use errorTemplate in decoding/complex.go --- internal/decoding/complex.go | 4 ++-- msgpack_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/decoding/complex.go b/internal/decoding/complex.go index 819c82e..3b4d741 100644 --- a/internal/decoding/complex.go +++ b/internal/decoding/complex.go @@ -58,7 +58,7 @@ func (d *decoder) asComplex64(offset int, k reflect.Kind) (complex64, int, error } - return complex(0, 0), 0, fmt.Errorf("should not reach this line!! code %x decoding %v", code, k) + return complex(0, 0), 0, d.errorTemplate(code, k) } func (d *decoder) asComplex128(offset int, k reflect.Kind) (complex128, int, error) { @@ -110,5 +110,5 @@ func (d *decoder) asComplex128(offset int, k reflect.Kind) (complex128, int, err } - return complex(0, 0), 0, fmt.Errorf("should not reach this line!! code %x decoding %v", code, k) + return complex(0, 0), 0, d.errorTemplate(code, k) } diff --git a/msgpack_test.go b/msgpack_test.go index 7a4cb48..d7103f5 100644 --- a/msgpack_test.go +++ b/msgpack_test.go @@ -380,7 +380,7 @@ func TestComplex(t *testing.T) { { n: "Nil", v: nil, - e: "should not reach this line", + e: "invalid code c0 decoding", }, } encdec(t, args...) From 8191431e03114b3cd5d5c3d3d96065c3871b19c1 Mon Sep 17 00:00:00 2001 From: shamaton Date: Tue, 6 Aug 2024 18:25:41 +0900 Subject: [PATCH 09/41] add ReadCount to some testcases --- internal/stream/decoding/map_test.go | 22 ++++++++++++++++++++++ internal/stream/decoding/string_test.go | 9 +++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/internal/stream/decoding/map_test.go b/internal/stream/decoding/map_test.go index d327974..f977f9c 100644 --- a/internal/stream/decoding/map_test.go +++ b/internal/stream/decoding/map_test.go @@ -78,6 +78,7 @@ func Test_asFixedMap_StringInt(t *testing.T) { Code: def.Int32, Data: []byte{def.Int32}, Expected: false, + ReadCount: 1, MethodAsCustom: method, IsTemplateError: true, }, @@ -86,6 +87,7 @@ func Test_asFixedMap_StringInt(t *testing.T) { Code: def.Str8, Data: []byte{def.FixStr + 1, 'a', def.Str8}, Expected: false, + ReadCount: 3, MethodAsCustom: method, IsTemplateError: true, }, @@ -133,6 +135,7 @@ func Test_asFixedMap_StringUint(t *testing.T) { Code: def.Int32, Data: []byte{def.Int32}, Expected: false, + ReadCount: 1, MethodAsCustom: method, IsTemplateError: true, }, @@ -141,6 +144,7 @@ func Test_asFixedMap_StringUint(t *testing.T) { Code: def.Str8, Data: []byte{def.FixStr + 1, 'a', def.Str8}, Expected: false, + ReadCount: 3, MethodAsCustom: method, IsTemplateError: true, }, @@ -188,6 +192,7 @@ func Test_asFixedMap_StringFloat(t *testing.T) { Code: def.Int32, Data: []byte{def.Int32}, Expected: false, + ReadCount: 1, MethodAsCustom: method, IsTemplateError: true, }, @@ -196,6 +201,7 @@ func Test_asFixedMap_StringFloat(t *testing.T) { Code: def.Str8, Data: []byte{def.FixStr + 1, 'a', def.Str8}, Expected: false, + ReadCount: 3, MethodAsCustom: method, IsTemplateError: true, }, @@ -234,6 +240,7 @@ func Test_asFixedMap_StringBool(t *testing.T) { Code: def.Int32, Data: []byte{def.Int32}, Expected: false, + ReadCount: 1, MethodAsCustom: method, IsTemplateError: true, }, @@ -242,6 +249,7 @@ func Test_asFixedMap_StringBool(t *testing.T) { Code: def.Str8, Data: []byte{def.FixStr + 1, 'a', def.Str8}, Expected: false, + ReadCount: 3, MethodAsCustom: method, IsTemplateError: true, }, @@ -277,6 +285,7 @@ func Test_asFixedMap_StringString(t *testing.T) { Code: def.Int32, Data: []byte{def.Int32}, Expected: false, + ReadCount: 1, MethodAsCustom: method, IsTemplateError: true, }, @@ -285,6 +294,7 @@ func Test_asFixedMap_StringString(t *testing.T) { Code: def.Int32, Data: []byte{def.FixStr + 1, 'a', def.Int32}, Expected: false, + ReadCount: 3, MethodAsCustom: method, IsTemplateError: true, }, @@ -320,6 +330,7 @@ func Test_asFixedMap_IntString(t *testing.T) { Code: def.Str8, Data: []byte{def.Str8}, Expected: false, + ReadCount: 1, MethodAsCustom: method, IsTemplateError: true, }, @@ -328,6 +339,7 @@ func Test_asFixedMap_IntString(t *testing.T) { Code: def.Int32, Data: []byte{def.Int8, dv, def.Int32}, Expected: false, + ReadCount: 3, MethodAsCustom: method, IsTemplateError: true, }, @@ -375,6 +387,7 @@ func Test_asFixedMap_IntBool(t *testing.T) { Code: def.Str8, Data: []byte{def.Str8}, Expected: false, + ReadCount: 1, MethodAsCustom: method, IsTemplateError: true, }, @@ -383,6 +396,7 @@ func Test_asFixedMap_IntBool(t *testing.T) { Code: def.Int32, Data: []byte{def.Int8, dv, def.Int32}, Expected: false, + ReadCount: 3, MethodAsCustom: method, IsTemplateError: true, }, @@ -430,6 +444,7 @@ func Test_asFixedMap_UintString(t *testing.T) { Code: def.Str8, Data: []byte{def.Str8}, Expected: false, + ReadCount: 1, MethodAsCustom: method, IsTemplateError: true, }, @@ -439,6 +454,7 @@ func Test_asFixedMap_UintString(t *testing.T) { Data: []byte{def.Uint8, dv, def.Int32}, Expected: false, MethodAsCustom: method, + ReadCount: 3, IsTemplateError: true, }, { @@ -485,6 +501,7 @@ func Test_asFixedMap_UintBool(t *testing.T) { Code: def.Str8, Data: []byte{def.Str8}, Expected: false, + ReadCount: 1, MethodAsCustom: method, IsTemplateError: true, }, @@ -493,6 +510,7 @@ func Test_asFixedMap_UintBool(t *testing.T) { Code: def.Int32, Data: []byte{def.Uint8, dv, def.Int32}, Expected: false, + ReadCount: 3, MethodAsCustom: method, IsTemplateError: true, }, @@ -540,6 +558,7 @@ func Test_asFixedMap_FloatString(t *testing.T) { Code: def.Str8, Data: []byte{def.Str8}, Expected: false, + ReadCount: 1, MethodAsCustom: method, IsTemplateError: true, }, @@ -548,6 +567,7 @@ func Test_asFixedMap_FloatString(t *testing.T) { Code: def.Int32, Data: []byte{def.Uint8, dv, def.Int32}, Expected: false, + ReadCount: 3, MethodAsCustom: method, IsTemplateError: true, }, @@ -586,6 +606,7 @@ func Test_asFixedMap_FloatBool(t *testing.T) { Code: def.Str8, Data: []byte{def.Str8}, Expected: false, + ReadCount: 1, MethodAsCustom: method, IsTemplateError: true, @@ -595,6 +616,7 @@ func Test_asFixedMap_FloatBool(t *testing.T) { Code: def.Int32, Data: []byte{def.Uint8, dv, def.Int32}, Expected: false, + ReadCount: 3, MethodAsCustom: method, IsTemplateError: true, diff --git a/internal/stream/decoding/string_test.go b/internal/stream/decoding/string_test.go index df941e2..9875af6 100644 --- a/internal/stream/decoding/string_test.go +++ b/internal/stream/decoding/string_test.go @@ -66,10 +66,11 @@ func Test_asString(t *testing.T) { } testcases := AsXXXTestCases[string]{ { - Name: "String.error", - Data: []byte{def.FixStr + 1}, - Error: io.EOF, - MethodAs: method, + Name: "String.error", + Data: []byte{def.FixStr + 1}, + Error: io.EOF, + ReadCount: 1, + MethodAs: method, }, { Name: "String.ok", From 912c5b172fca9d8f0260949a3d591f51575265c8 Mon Sep 17 00:00:00 2001 From: shamaton Date: Tue, 6 Aug 2024 18:30:07 +0900 Subject: [PATCH 10/41] fix TestComplex --- msgpack_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msgpack_test.go b/msgpack_test.go index d7103f5..f5a7cb3 100644 --- a/msgpack_test.go +++ b/msgpack_test.go @@ -416,7 +416,7 @@ func TestComplex(t *testing.T) { { n: "Nil", v: nil, - e: "should not reach this line", + e: "invalid code c0 decoding", }, } encdec(t, args...) From fa57eea81f967831e08686783095ae732050f171 Mon Sep 17 00:00:00 2001 From: shamaton Date: Tue, 6 Aug 2024 22:50:06 +0900 Subject: [PATCH 11/41] add decoding interface tests --- internal/common/testutil/testutil.go | 5 +- internal/stream/decoding/decoding_test.go | 4 +- internal/stream/decoding/interface.go | 12 +- internal/stream/decoding/interface_test.go | 220 +++++++++++++++++++++ internal/stream/decoding/string_test.go | 44 +++-- 5 files changed, 256 insertions(+), 29 deletions(-) create mode 100644 internal/stream/decoding/interface_test.go diff --git a/internal/common/testutil/testutil.go b/internal/common/testutil/testutil.go index d38d0e5..1c67cf7 100644 --- a/internal/common/testutil/testutil.go +++ b/internal/common/testutil/testutil.go @@ -2,6 +2,7 @@ package testutil import ( "errors" + "reflect" "strings" "testing" ) @@ -37,9 +38,9 @@ func ErrorContains(t *testing.T, err error, errStr string) { } } -func Equal[T comparable](t *testing.T, actual, expected T) { +func Equal[T any](t *testing.T, actual, expected T) { t.Helper() - if actual != expected { + if !reflect.DeepEqual(actual, expected) { t.Fatalf("not equal. actual: %v, expected: %v", actual, expected) } } diff --git a/internal/stream/decoding/decoding_test.go b/internal/stream/decoding/decoding_test.go index 0ea5c9b..2429cca 100644 --- a/internal/stream/decoding/decoding_test.go +++ b/internal/stream/decoding/decoding_test.go @@ -10,7 +10,7 @@ import ( tu "github.com/shamaton/msgpack/v2/internal/common/testutil" ) -type AsXXXTestCase[T comparable] struct { +type AsXXXTestCase[T any] struct { Name string Code byte Data []byte @@ -23,7 +23,7 @@ type AsXXXTestCase[T comparable] struct { MethodAsCustom func(d *decoder) (T, error) } -type AsXXXTestCases[T comparable] []AsXXXTestCase[T] +type AsXXXTestCases[T any] []AsXXXTestCase[T] func (tc *AsXXXTestCase[T]) Run(t *testing.T) { const kind = reflect.String diff --git a/internal/stream/decoding/interface.go b/internal/stream/decoding/interface.go index c2c2af7..d0a0a11 100644 --- a/internal/stream/decoding/interface.go +++ b/internal/stream/decoding/interface.go @@ -1,6 +1,7 @@ package decoding import ( + "errors" "fmt" "reflect" @@ -10,7 +11,7 @@ import ( func (d *decoder) asInterface(k reflect.Kind) (interface{}, error) { code, err := d.readSize1() if err != nil { - return 0, err + return nil, err } return d.asInterfaceWithCode(code, k) } @@ -133,7 +134,7 @@ func (d *decoder) asInterfaceWithCode(code byte, k reflect.Kind) (interface{}, e return 0, err } - if d.canSetAsMapKey(keyCode) != nil { + if err := d.canSetAsMapKey(keyCode); err != nil { return nil, err } key, err := d.asInterfaceWithCode(keyCode, k) @@ -166,12 +167,15 @@ func (d *decoder) asInterfaceWithCode(code byte, k reflect.Kind) (interface{}, e return nil, d.errorTemplate(code, k) } +var ErrCanNotSetSliceAsMapKey = errors.New("can not set slice as map key") +var ErrCanNotSetMapAsMapKey = errors.New("can not set map as map key") + func (d *decoder) canSetAsMapKey(code byte) error { switch { case d.isFixSlice(code), code == def.Array16, code == def.Array32: - return fmt.Errorf("can not use slice code for map key/ code: %x", code) + return fmt.Errorf("%w. code: %x", ErrCanNotSetSliceAsMapKey, code) case d.isFixMap(code), code == def.Map16, code == def.Map32: - return fmt.Errorf("can not use map code for map key/ code: %x", code) + return fmt.Errorf("%w. code: %x", ErrCanNotSetMapAsMapKey, code) } return nil } diff --git a/internal/stream/decoding/interface_test.go b/internal/stream/decoding/interface_test.go new file mode 100644 index 0000000..f7ed807 --- /dev/null +++ b/internal/stream/decoding/interface_test.go @@ -0,0 +1,220 @@ +package decoding + +import ( + "fmt" + "io" + "reflect" + "testing" + + "github.com/shamaton/msgpack/v2/def" + "github.com/shamaton/msgpack/v2/ext" +) + +func Test_asInterface(t *testing.T) { + method := func(d *decoder) func(reflect.Kind) (any, error) { + return d.asInterface + } + testcases := AsXXXTestCases[any]{ + { + Name: "error", + Data: []byte{}, + Error: io.EOF, + ReadCount: 0, + MethodAs: method, + }, + { + Name: "ok", + Data: []byte{def.Nil}, + Expected: nil, + ReadCount: 1, + MethodAs: method, + }, + } + + for _, tc := range testcases { + tc.Run(t) + } +} + +func Test_asInterfaceWithCode(t *testing.T) { + dec := testExt2StreamDecoder{} + AddExtDecoder(&dec) + defer RemoveExtDecoder(&dec) + + method := func(d *decoder) func(byte, reflect.Kind) (any, error) { + return d.asInterfaceWithCode + } + testcases := AsXXXTestCases[any]{ + { + Name: "Uint8.error", + Code: def.Uint8, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Uint16.error", + Code: def.Uint16, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Uint32.error", + Code: def.Uint32, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Uint64.error", + Code: def.Uint64, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Int8.error", + Code: def.Int8, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Int16.error", + Code: def.Int16, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Int32.error", + Code: def.Int32, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Int64.error", + Code: def.Int64, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Float32.error", + Code: def.Float32, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Float64.error", + Code: def.Float64, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Str.error", + Code: def.Str8, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Bin.error", + Code: def.Bin8, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Array.error.length", + Code: def.Array16, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Array.error.set", + Code: def.Array16, + Data: []byte{0, 1}, + ReadCount: 1, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Map.error.length", + Code: def.Map16, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Map.error.set.key_code", + Code: def.Map16, + Data: []byte{0, 1}, + ReadCount: 1, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Map.error.set.can.slice", + Code: def.Map16, + Data: []byte{0, 1, def.Array16}, + ReadCount: 2, + Error: ErrCanNotSetSliceAsMapKey, + MethodAsWithCode: method, + }, + { + Name: "Map.error.set.can.map", + Code: def.Map16, + Data: []byte{0, 1, def.Map16}, + ReadCount: 2, + Error: ErrCanNotSetMapAsMapKey, + MethodAsWithCode: method, + }, + { + Name: "Map.error.set.key", + Code: def.Map16, + Data: []byte{0, 1, def.FixStr + 1}, + ReadCount: 2, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Map.error.set.value", + Code: def.Map16, + Data: []byte{0, 1, def.FixStr + 1, 'a'}, + ReadCount: 3, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Ext.error", + Code: def.Fixext1, + Data: []byte{3, 0}, + ReadCount: 2, + Error: ErrTestExtStreamDecoder, + MethodAsWithCode: method, + }, + { + Name: "Unexpected", + Code: def.Fixext1, + Data: []byte{4, 0}, + ReadCount: 2, + IsTemplateError: true, + MethodAsWithCode: method, + }, + } + + for _, tc := range testcases { + tc.Run(t) + } +} + +// TODO: to testutil +type testExt2StreamDecoder struct{} + +var _ ext.StreamDecoder = (*testExt2StreamDecoder)(nil) + +func (td *testExt2StreamDecoder) Code() int8 { + return 3 +} + +func (td *testExt2StreamDecoder) IsType(_ byte, code int8, _ int) bool { + return code == td.Code() +} + +var ErrTestExtStreamDecoder = fmt.Errorf("testExtStreamDecoder") + +func (td *testExt2StreamDecoder) ToValue(_ byte, _ []byte, k reflect.Kind) (any, error) { + return nil, ErrTestExtStreamDecoder +} diff --git a/internal/stream/decoding/string_test.go b/internal/stream/decoding/string_test.go index 9875af6..db33cfc 100644 --- a/internal/stream/decoding/string_test.go +++ b/internal/stream/decoding/string_test.go @@ -1,15 +1,12 @@ package decoding import ( - "bytes" "io" "math" "reflect" "testing" "github.com/shamaton/msgpack/v2/def" - "github.com/shamaton/msgpack/v2/internal/common" - tu "github.com/shamaton/msgpack/v2/internal/common/testutil" ) func Test_stringByteLength(t *testing.T) { @@ -87,22 +84,27 @@ func Test_asString(t *testing.T) { } func Test_asStringByte(t *testing.T) { - t.Run("read error", func(t *testing.T) { - d := decoder{ - r: tu.NewErrReader(), - buf: common.GetBuffer(), - } - v, err := d.asStringByte(reflect.String) - tu.IsError(t, err, tu.ErrReaderErr) - tu.EqualSlice(t, v, emptyBytes) - }) - t.Run("ok", func(t *testing.T) { - d := decoder{ - r: bytes.NewReader([]byte{def.FixStr + 1, 'a'}), - buf: common.GetBuffer(), - } - v, err := d.asStringByte(reflect.String) - tu.NoError(t, err) - tu.EqualSlice(t, v, []byte("a")) - }) + method := func(d *decoder) func(reflect.Kind) ([]byte, error) { + return d.asStringByte + } + testcases := AsXXXTestCases[[]byte]{ + { + Name: "error", + Data: []byte{def.FixStr + 1}, + Error: io.EOF, + ReadCount: 1, + MethodAs: method, + }, + { + Name: "ok", + Data: []byte{def.FixStr + 1, 'a'}, + Expected: []byte{'a'}, + ReadCount: 2, + MethodAs: method, + }, + } + + for _, tc := range testcases { + tc.Run(t) + } } From a201ea4dc00391d2756b019ebb277c05bc32e13c Mon Sep 17 00:00:00 2001 From: shamaton Date: Wed, 7 Aug 2024 08:07:52 +0900 Subject: [PATCH 12/41] add decoding bin tests --- internal/stream/decoding/bin_test.go | 103 +++++++++++++++++++++ internal/stream/decoding/interface_test.go | 16 ++++ 2 files changed, 119 insertions(+) create mode 100644 internal/stream/decoding/bin_test.go diff --git a/internal/stream/decoding/bin_test.go b/internal/stream/decoding/bin_test.go new file mode 100644 index 0000000..e205109 --- /dev/null +++ b/internal/stream/decoding/bin_test.go @@ -0,0 +1,103 @@ +package decoding + +import ( + "io" + "reflect" + "testing" + + "github.com/shamaton/msgpack/v2/def" + tu "github.com/shamaton/msgpack/v2/internal/common/testutil" +) + +func Test_isCodeBin(t *testing.T) { + d := decoder{} + for i := 0x00; i <= 0xff; i++ { + v := byte(i) + isBin := v == def.Bin8 || v == def.Bin16 || v == def.Bin32 + tu.Equal(t, d.isCodeBin(v), isBin) + } +} + +func Test_asBinWithCode(t *testing.T) { + method := func(d *decoder) func(byte, reflect.Kind) ([]byte, error) { + return d.asBinWithCode + } + testcases := AsXXXTestCases[[]byte]{ + { + Name: "Bin8.error.size", + Code: def.Bin8, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Bin8.error.data", + Code: def.Bin8, + Data: []byte{1}, + ReadCount: 1, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Bin8.ok", + Code: def.Bin8, + Data: []byte{1, 'a'}, + Expected: []byte{'a'}, + ReadCount: 2, + MethodAsWithCode: method, + }, + { + Name: "Bin16.error", + Code: def.Bin16, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Bin16.error.data", + Code: def.Bin16, + Data: []byte{0, 1}, + ReadCount: 1, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Bin16.ok", + Code: def.Bin16, + Data: []byte{0, 1, 'b'}, + Expected: []byte{'b'}, + ReadCount: 2, + MethodAsWithCode: method, + }, + { + Name: "Bin32.error", + Code: def.Bin32, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Bin32.error.data", + Code: def.Bin32, + Data: []byte{0, 0, 0, 1}, + ReadCount: 1, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Bin32.ok", + Code: def.Bin32, + Data: []byte{0, 0, 0, 1, 'c'}, + Expected: []byte{'c'}, + ReadCount: 2, + MethodAsWithCode: method, + }, + { + Name: "Unexpected", + Code: def.Nil, + IsTemplateError: true, + MethodAsWithCode: method, + }, + } + + for _, tc := range testcases { + tc.Run(t) + } +} diff --git a/internal/stream/decoding/interface_test.go b/internal/stream/decoding/interface_test.go index f7ed807..42e7b1f 100644 --- a/internal/stream/decoding/interface_test.go +++ b/internal/stream/decoding/interface_test.go @@ -99,6 +99,14 @@ func Test_asInterfaceWithCode(t *testing.T) { Error: io.EOF, MethodAsWithCode: method, }, + { + Name: "Float32.ok", + Code: def.Float32, + Data: []byte{63, 128, 0, 0}, + Expected: float32(1), + ReadCount: 1, + MethodAsWithCode: method, + }, { Name: "Float64.error", Code: def.Float64, @@ -180,6 +188,14 @@ func Test_asInterfaceWithCode(t *testing.T) { { Name: "Ext.error", Code: def.Fixext1, + Data: []byte{}, + ReadCount: 0, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "ExtCoder.error", + Code: def.Fixext1, Data: []byte{3, 0}, ReadCount: 2, Error: ErrTestExtStreamDecoder, From 2226b138b12d5f1fab6599fea0be94a286cbe43a Mon Sep 17 00:00:00 2001 From: shamaton Date: Wed, 7 Aug 2024 22:30:58 +0900 Subject: [PATCH 13/41] add decoding float tests, struct tests --- internal/common/testutil/testutil.go | 11 + internal/stream/decoding/decoding.go | 5 +- internal/stream/decoding/float.go | 8 +- internal/stream/decoding/float_test.go | 204 +++++++ internal/stream/decoding/struct.go | 2 +- internal/stream/decoding/struct_test.go | 724 ++++++++++++++++++++++++ 6 files changed, 948 insertions(+), 6 deletions(-) create mode 100644 internal/stream/decoding/float_test.go create mode 100644 internal/stream/decoding/struct_test.go diff --git a/internal/common/testutil/testutil.go b/internal/common/testutil/testutil.go index 1c67cf7..9420e23 100644 --- a/internal/common/testutil/testutil.go +++ b/internal/common/testutil/testutil.go @@ -66,3 +66,14 @@ func EqualMap[K comparable, V comparable](t *testing.T, actual, expected map[K]V } } } + +type Equaler[T any] interface { + Equal(other T) bool +} + +func EqualEqualer[T Equaler[T]](t *testing.T, actual, expected T) { + t.Helper() + if !actual.Equal(expected) { + t.Fatalf("not equal. actual: %v, expected: %v", actual, expected) + } +} diff --git a/internal/stream/decoding/decoding.go b/internal/stream/decoding/decoding.go index ef4801c..cd220af 100644 --- a/internal/stream/decoding/decoding.go +++ b/internal/stream/decoding/decoding.go @@ -1,6 +1,7 @@ package decoding import ( + "errors" "fmt" "io" "reflect" @@ -319,6 +320,8 @@ func (d *decoder) decodeWithCode(code byte, rv reflect.Value) error { return nil } +var ErrCanNotDecode = errors.New("msgpack : invalid code") + func (d *decoder) errorTemplate(code byte, k reflect.Kind) error { - return fmt.Errorf("msgpack : invalid code %x decoding %v", code, k) + return fmt.Errorf("msgpack : invalid code %x decoding %v, %w", code, k, ErrCanNotDecode) } diff --git a/internal/stream/decoding/float.go b/internal/stream/decoding/float.go index e720708..51c1887 100644 --- a/internal/stream/decoding/float.go +++ b/internal/stream/decoding/float.go @@ -29,14 +29,14 @@ func (d *decoder) asFloat32WithCode(code byte, k reflect.Kind) (float32, error) case d.isPositiveFixNum(code), code == def.Uint8, code == def.Uint16, code == def.Uint32, code == def.Uint64: v, err := d.asUintWithCode(code, k) if err != nil { - break + return 0, err } return float32(v), nil case d.isNegativeFixNum(code), code == def.Int8, code == def.Int16, code == def.Int32, code == def.Int64: v, err := d.asIntWithCode(code, k) if err != nil { - break + return 0, err } return float32(v), nil @@ -75,14 +75,14 @@ func (d *decoder) asFloat64WithCode(code byte, k reflect.Kind) (float64, error) case d.isPositiveFixNum(code), code == def.Uint8, code == def.Uint16, code == def.Uint32, code == def.Uint64: v, err := d.asUintWithCode(code, k) if err != nil { - break + return 0, err } return float64(v), nil case d.isNegativeFixNum(code), code == def.Int8, code == def.Int16, code == def.Int32, code == def.Int64: v, err := d.asIntWithCode(code, k) if err != nil { - break + return 0, err } return float64(v), nil diff --git a/internal/stream/decoding/float_test.go b/internal/stream/decoding/float_test.go new file mode 100644 index 0000000..eecba05 --- /dev/null +++ b/internal/stream/decoding/float_test.go @@ -0,0 +1,204 @@ +package decoding + +import ( + "github.com/shamaton/msgpack/v2/def" + "io" + "reflect" + "testing" +) + +func Test_asFloat32(t *testing.T) { + method := func(d *decoder) func(reflect.Kind) (float32, error) { + return d.asFloat32 + } + testcases := AsXXXTestCases[float32]{ + { + Name: "error", + Error: io.EOF, + MethodAs: method, + }, + { + Name: "ok", + Data: []byte{def.Float32, 63, 128, 0, 0}, + Expected: float32(1), + ReadCount: 2, + MethodAs: method, + }, + } + + for _, tc := range testcases { + tc.Run(t) + } +} + +func Test_asFloat32WithCode(t *testing.T) { + method := func(d *decoder) func(byte, reflect.Kind) (float32, error) { + return d.asFloat32WithCode + } + testcases := AsXXXTestCases[float32]{ + { + Name: "Float32.error", + Code: def.Float32, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Float32.ok", + Code: def.Float32, + Data: []byte{63, 128, 0, 0}, + Expected: float32(1), + ReadCount: 1, + MethodAsWithCode: method, + }, + { + Name: "Uint8.error", + Code: def.Uint8, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Uint8.ok", + Code: def.Uint8, + Data: []byte{1}, + Expected: float32(1), + ReadCount: 1, + MethodAsWithCode: method, + }, + { + Name: "Int8.error", + Code: def.Int8, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Int8.ok", + Code: def.Int8, + Data: []byte{0xff}, + Expected: float32(-1), + ReadCount: 1, + MethodAsWithCode: method, + }, + { + Name: "Nil.ok", + Code: def.Nil, + Expected: float32(0), + ReadCount: 0, + MethodAsWithCode: method, + }, + { + Name: "Unexpected", + Code: def.Str8, + IsTemplateError: true, + MethodAsWithCode: method, + }, + } + + for _, tc := range testcases { + tc.Run(t) + } +} + +func Test_asFloat64(t *testing.T) { + method := func(d *decoder) func(reflect.Kind) (float64, error) { + return d.asFloat64 + } + testcases := AsXXXTestCases[float64]{ + { + Name: "error", + Error: io.EOF, + MethodAs: method, + }, + { + Name: "ok", + Data: []byte{def.Float64, 63, 240, 0, 0, 0, 0, 0, 0}, + Expected: float64(1), + ReadCount: 2, + MethodAs: method, + }, + } + + for _, tc := range testcases { + tc.Run(t) + } +} + +func Test_asFloat64WithCode(t *testing.T) { + method := func(d *decoder) func(byte, reflect.Kind) (float64, error) { + return d.asFloat64WithCode + } + testcases := AsXXXTestCases[float64]{ + { + Name: "Float64.error", + Code: def.Float64, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Float64.ok", + Code: def.Float64, + Data: []byte{63, 240, 0, 0, 0, 0, 0, 0}, + Expected: float64(1), + ReadCount: 1, + MethodAsWithCode: method, + }, + { + Name: "Float32.error", + Code: def.Float32, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Float32.ok", + Code: def.Float32, + Data: []byte{63, 128, 0, 0}, + Expected: float64(1), + ReadCount: 1, + MethodAsWithCode: method, + }, + { + Name: "Uint8.error", + Code: def.Uint8, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Uint8.ok", + Code: def.Uint8, + Data: []byte{1}, + Expected: float64(1), + ReadCount: 1, + MethodAsWithCode: method, + }, + { + Name: "Int8.error", + Code: def.Int8, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Int8.ok", + Code: def.Int8, + Data: []byte{0xff}, + Expected: float64(-1), + ReadCount: 1, + MethodAsWithCode: method, + }, + { + Name: "Nil.ok", + Code: def.Nil, + Expected: float64(0), + ReadCount: 0, + MethodAsWithCode: method, + }, + { + Name: "Unexpected", + Code: def.Str8, + IsTemplateError: true, + MethodAsWithCode: method, + }, + } + + for _, tc := range testcases { + tc.Run(t) + } +} diff --git a/internal/stream/decoding/struct.go b/internal/stream/decoding/struct.go index 45221d1..e835be0 100644 --- a/internal/stream/decoding/struct.go +++ b/internal/stream/decoding/struct.go @@ -275,7 +275,7 @@ func (d *decoder) jumpOffset() error { case code == def.Fixext16: _, err = d.readSizeN(def.Byte1 + def.Byte16) return err - + case code == def.Ext8: b, err := d.readSize1() if err != nil { diff --git a/internal/stream/decoding/struct_test.go b/internal/stream/decoding/struct_test.go new file mode 100644 index 0000000..f54b4b6 --- /dev/null +++ b/internal/stream/decoding/struct_test.go @@ -0,0 +1,724 @@ +package decoding + +import ( + "io" + "reflect" + "testing" + "time" + + tu "github.com/shamaton/msgpack/v2/internal/common/testutil" + + "github.com/shamaton/msgpack/v2/def" +) + +func Test_setStruct_ext(t *testing.T) { + run := func(t *testing.T, rv reflect.Value) { + + method := func(d *decoder) func(byte, reflect.Kind) (any, error) { + return func(code byte, k reflect.Kind) (any, error) { + return nil, d.setStruct(code, rv, k) + } + } + + testcases := AsXXXTestCases[any]{ + { + Name: "Ext.error", + Code: def.Fixext1, + Data: []byte{}, + ReadCount: 0, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "ExtCoder.error", + Code: def.Fixext1, + Data: []byte{3, 0}, + ReadCount: 2, + Error: ErrTestExtStreamDecoder, + MethodAsWithCode: method, + }, + { + Name: "ExtCoder.ok", + Code: def.Fixext4, + Data: []byte{255, 0, 0, 0, 0}, + ReadCount: 2, + MethodAsWithCode: method, + }, + } + for _, tc := range testcases { + tc.Run(t) + } + } + + ngDec := testExt2StreamDecoder{} + AddExtDecoder(&ngDec) + defer RemoveExtDecoder(&ngDec) + + v1 := new(time.Time) + run(t, reflect.ValueOf(v1).Elem()) + tu.EqualEqualer(t, *v1, time.Unix(0, 0)) +} + +func Test_setStructFromMap(t *testing.T) { + run := func(t *testing.T, rv reflect.Value) { + method := func(d *decoder) func(byte, reflect.Kind) (any, error) { + return func(code byte, k reflect.Kind) (any, error) { + return nil, d.setStructFromMap(code, rv, k) + } + } + + testcases := AsXXXTestCases[any]{ + { + Name: "error.length", + Code: def.Map16, + Data: []byte{}, + ReadCount: 0, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "error.key", + Code: def.Map16, + Data: []byte{0, 1}, + ReadCount: 1, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "error.decode", + Code: def.Map16, + Data: []byte{0, 1, def.FixStr + 1, 'v', def.Array16}, + ReadCount: 4, + Error: ErrCanNotDecode, + MethodAsWithCode: method, + }, + { + Name: "error.jump", + Code: def.Map16, + Data: []byte{0, 1, def.FixStr + 1, 'a', 0xc1}, + ReadCount: 4, + MethodAsWithCode: method, + }, + { + Name: "ok", + Code: def.Map16, + Data: []byte{0, 1, def.FixStr + 1, 'v', def.PositiveFixIntMin + 7}, + ReadCount: 4, + MethodAsWithCode: method, + }, + } + for _, tc := range testcases { + tc.Run(t) + } + } + + type st struct { + V int `msgpack:"v"` + } + + v1 := new(st) + run(t, reflect.ValueOf(v1).Elem()) + tu.Equal(t, v1.V, 7) +} + +func Test_setStructFromArray(t *testing.T) { + run := func(t *testing.T, rv reflect.Value) { + method := func(d *decoder) func(byte, reflect.Kind) (any, error) { + return func(code byte, k reflect.Kind) (any, error) { + return nil, d.setStructFromArray(code, rv, k) + } + } + + testcases := AsXXXTestCases[any]{ + { + Name: "error.length", + Code: def.Array16, + Data: []byte{}, + ReadCount: 0, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "error.key", + Code: def.Array16, + Data: []byte{0, 1}, + ReadCount: 1, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "error.decode", + Code: def.Array16, + Data: []byte{0, 1, def.Array16}, + ReadCount: 2, + Error: ErrCanNotDecode, + MethodAsWithCode: method, + }, + { + Name: "error.jump", + Code: def.Array16, + Data: []byte{0, 2, 0, 0xc1}, + ReadCount: 3, + MethodAsWithCode: method, + }, + { + Name: "ok", + Code: def.Array16, + Data: []byte{0, 1, def.PositiveFixIntMin + 8}, + ReadCount: 2, + MethodAsWithCode: method, + }, + } + for _, tc := range testcases { + tc.Run(t) + } + } + + type st struct { + V int `msgpack:"v"` + } + + v1 := new(st) + run(t, reflect.ValueOf(v1).Elem()) + tu.Equal(t, v1.V, 8) +} +func Test_jumpOffset(t *testing.T) { + method := func(d *decoder) (any, error) { + return nil, d.jumpOffset() + } + + testcases := AsXXXTestCases[any]{ + { + Name: "error.read.code", + Data: []byte{}, + ReadCount: 0, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "True.ok", + Data: []byte{def.True}, + ReadCount: 1, + MethodAsCustom: method, + }, + { + Name: "False.ok", + Data: []byte{def.False}, + ReadCount: 1, + MethodAsCustom: method, + }, + { + Name: "PositiveFixNum.ok", + Data: []byte{def.PositiveFixIntMin + 1}, + ReadCount: 1, + MethodAsCustom: method, + }, + { + Name: "NegativeFixNum.ok", + Data: []byte{0xf0}, + ReadCount: 1, + MethodAsCustom: method, + }, + { + Name: "Uint8.error", + Data: []byte{def.Uint8}, + ReadCount: 1, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Int8.error", + Data: []byte{def.Int8}, + ReadCount: 1, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Uint8.ok", + Data: []byte{def.Uint8, 1}, + ReadCount: 2, + MethodAsCustom: method, + }, + { + Name: "Int8.ok", + Data: []byte{def.Int8, 1}, + ReadCount: 2, + MethodAsCustom: method, + }, + { + Name: "Uint16.error", + Data: []byte{def.Uint16}, + ReadCount: 1, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Int16.error", + Data: []byte{def.Int16}, + ReadCount: 1, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Uint16.ok", + Data: []byte{def.Uint16, 0, 1}, + ReadCount: 2, + MethodAsCustom: method, + }, + { + Name: "Int16.ok", + Data: []byte{def.Int16, 0, 1}, + ReadCount: 2, + MethodAsCustom: method, + }, + { + Name: "Uint32.error", + Data: []byte{def.Uint32}, + ReadCount: 1, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Int32.error", + Data: []byte{def.Int32}, + ReadCount: 1, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Float32.error", + Data: []byte{def.Float32}, + ReadCount: 1, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Uint32.ok", + Data: []byte{def.Uint32, 0, 0, 0, 0}, + ReadCount: 2, + MethodAsCustom: method, + }, + { + Name: "Int32.ok", + Data: []byte{def.Int32, 0, 0, 0, 0}, + ReadCount: 2, + MethodAsCustom: method, + }, + { + Name: "Float32.ok", + Data: []byte{def.Float32, 0, 0, 0, 0}, + ReadCount: 2, + MethodAsCustom: method, + }, + { + Name: "Uint64.error", + Data: []byte{def.Uint64}, + ReadCount: 1, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Int64.error", + Data: []byte{def.Int64}, + ReadCount: 1, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Float64.error", + Data: []byte{def.Float64}, + ReadCount: 1, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Uint64.ok", + Data: []byte{def.Uint64, 0, 0, 0, 0, 0, 0, 0, 0}, + ReadCount: 2, + MethodAsCustom: method, + }, + { + Name: "Int64.ok", + Data: []byte{def.Int64, 0, 0, 0, 0, 0, 0, 0, 0}, + ReadCount: 2, + MethodAsCustom: method, + }, + { + Name: "Float64.ok", + Data: []byte{def.Float64, 0, 0, 0, 0, 0, 0, 0, 0}, + ReadCount: 2, + MethodAsCustom: method, + }, + { + Name: "FixStr.ng", + Data: []byte{def.FixStr + 1}, + ReadCount: 1, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "FixStr.ok", + Data: []byte{def.FixStr + 1, 0}, + ReadCount: 2, + MethodAsCustom: method, + }, + { + Name: "Str8.ng.length", + Data: []byte{def.Str8}, + ReadCount: 1, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Str8.ng.str", + Data: []byte{def.Str8, 1}, + ReadCount: 2, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Str8.ok", + Data: []byte{def.Str8, 1, 'a'}, + ReadCount: 3, + MethodAsCustom: method, + }, + { + Name: "Bin8.ng.length", + Data: []byte{def.Bin8}, + ReadCount: 1, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Bin8.ng.str", + Data: []byte{def.Bin8, 1}, + ReadCount: 2, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Bin8.ok", + Data: []byte{def.Bin8, 1, 'a'}, + ReadCount: 3, + MethodAsCustom: method, + }, + { + Name: "Str16.ng.length", + Data: []byte{def.Str16}, + ReadCount: 1, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Str16.ng.str", + Data: []byte{def.Str16, 0, 1}, + ReadCount: 2, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Str16.ok", + Data: []byte{def.Str16, 0, 1, 'a'}, + ReadCount: 3, + MethodAsCustom: method, + }, + { + Name: "Bin16.ng.length", + Data: []byte{def.Bin16}, + ReadCount: 1, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Bin16.ng.str", + Data: []byte{def.Bin16, 0, 1}, + ReadCount: 2, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Bin16.ok", + Data: []byte{def.Bin16, 0, 1, 'a'}, + ReadCount: 3, + MethodAsCustom: method, + }, + { + Name: "Str32.ng.length", + Data: []byte{def.Str32}, + ReadCount: 1, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Str32.ng.str", + Data: []byte{def.Str32, 0, 0, 0, 1}, + ReadCount: 2, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Str32.ok", + Data: []byte{def.Str32, 0, 0, 0, 1, 'a'}, + ReadCount: 3, + MethodAsCustom: method, + }, + { + Name: "Bin32.ng.length", + Data: []byte{def.Bin32}, + ReadCount: 1, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Bin32.ng.str", + Data: []byte{def.Bin32, 0, 0, 0, 1}, + ReadCount: 2, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Bin32.ok", + Data: []byte{def.Bin32, 0, 0, 0, 1, 'a'}, + ReadCount: 3, + MethodAsCustom: method, + }, + { + Name: "FixSlice.ng", + Data: []byte{def.FixArray + 1}, + ReadCount: 1, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "FixSlice.ok", + Data: []byte{def.FixArray + 1, 0xc1}, + ReadCount: 2, + MethodAsCustom: method, + }, + { + Name: "Array16.ng.len", + Data: []byte{def.Array16}, + ReadCount: 1, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Array16.ng.jump", + Data: []byte{def.Array16, 0, 1}, + ReadCount: 2, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Array16.ok", + Data: []byte{def.Array16, 0, 1, 0xc1}, + ReadCount: 3, + MethodAsCustom: method, + }, { + Name: "Array32.ng.len", + Data: []byte{def.Array32}, + ReadCount: 1, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Array32.ng.jump", + Data: []byte{def.Array32, 0, 0, 0, 1}, + ReadCount: 2, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Array32.ok", + Data: []byte{def.Array32, 0, 0, 0, 1, 0xc1}, + ReadCount: 3, + MethodAsCustom: method, + }, + { + Name: "FixMap.ng", + Data: []byte{def.FixMap + 1}, + ReadCount: 1, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "FixMap.ok", + Data: []byte{def.FixMap + 1, 0xc1, 0xc1}, + ReadCount: 3, + MethodAsCustom: method, + }, + { + Name: "Map16.ng.len", + Data: []byte{def.Map16}, + ReadCount: 1, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Map16.ng.jump", + Data: []byte{def.Map16, 0, 1}, + ReadCount: 2, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Map16.ok", + Data: []byte{def.Map16, 0, 1, 0xc1, 0xc1}, + ReadCount: 4, + MethodAsCustom: method, + }, { + Name: "Map32.ng.len", + Data: []byte{def.Map32}, + ReadCount: 1, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Map32.ng.jump", + Data: []byte{def.Map32, 0, 0, 0, 1}, + ReadCount: 2, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Map32.ok", + Data: []byte{def.Map32, 0, 0, 0, 1, 0xc1, 0xc1}, + ReadCount: 4, + MethodAsCustom: method, + }, + { + Name: "Fixext1.ng", + Data: []byte{def.Fixext1}, + ReadCount: 1, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Fixext1.ok", + Data: []byte{def.Fixext1, 0, 0}, + ReadCount: 2, + MethodAsCustom: method, + }, + { + Name: "Fixext2.ng", + Data: []byte{def.Fixext2}, + ReadCount: 1, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Fixext2.ok", + Data: []byte{def.Fixext2, 0, 0, 0}, + ReadCount: 2, + MethodAsCustom: method, + }, + { + Name: "Fixext4.ng", + Data: []byte{def.Fixext4}, + ReadCount: 1, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Fixext4.ok", + Data: []byte{def.Fixext4, 0, 0, 0, 0, 0}, + ReadCount: 2, + MethodAsCustom: method, + }, + { + Name: "Fixext8.ng", + Data: []byte{def.Fixext8}, + ReadCount: 1, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Fixext8.ok", + Data: []byte{def.Fixext8, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + ReadCount: 2, + MethodAsCustom: method, + }, + { + Name: "Fixext16.ng", + Data: []byte{def.Fixext16}, + ReadCount: 1, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Fixext16.ok", + Data: []byte{def.Fixext16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + ReadCount: 2, + MethodAsCustom: method, + }, + { + Name: "Ext8.ng.size", + Data: []byte{def.Ext8}, + ReadCount: 1, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Ext8.ng.size.n", + Data: []byte{def.Ext8, 1}, + ReadCount: 2, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Ext8.ok", + Data: []byte{def.Ext8, 1, 0}, + ReadCount: 3, + MethodAsCustom: method, + }, + { + Name: "Ext16.ng.size", + Data: []byte{def.Ext16}, + ReadCount: 1, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Ext16.ng.size.n", + Data: []byte{def.Ext16, 0, 1}, + ReadCount: 2, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Ext16.ok", + Data: []byte{def.Ext16, 0, 1, 0}, + ReadCount: 3, + MethodAsCustom: method, + }, + { + Name: "Ext32.ng.size", + Data: []byte{def.Ext32}, + ReadCount: 1, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Ext32.ng.size.n", + Data: []byte{def.Ext32, 0, 0, 0, 1}, + ReadCount: 2, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "Ext32.ok", + Data: []byte{def.Ext32, 0, 0, 0, 1, 0}, + ReadCount: 3, + MethodAsCustom: method, + }, + { + Name: "Unexpected", + Data: []byte{0xc1}, + ReadCount: 1, + MethodAsCustom: method, + }, + } + for _, tc := range testcases { + tc.Run(t) + } +} From 3f12965f33be3a27ed9735434c61d13ef9674a44 Mon Sep 17 00:00:00 2001 From: shamaton Date: Wed, 7 Aug 2024 23:58:00 +0900 Subject: [PATCH 14/41] add decoding int tests --- internal/stream/decoding/int_test.go | 198 ++++++++++++++++++++++++ internal/stream/decoding/struct_test.go | 10 +- 2 files changed, 204 insertions(+), 4 deletions(-) create mode 100644 internal/stream/decoding/int_test.go diff --git a/internal/stream/decoding/int_test.go b/internal/stream/decoding/int_test.go new file mode 100644 index 0000000..5a45e5a --- /dev/null +++ b/internal/stream/decoding/int_test.go @@ -0,0 +1,198 @@ +package decoding + +import ( + "io" + "reflect" + "testing" + + "github.com/shamaton/msgpack/v2/def" +) + +func Test_asInt(t *testing.T) { + method := func(d *decoder) func(reflect.Kind) (int64, error) { + return d.asInt + } + testcases := AsXXXTestCases[int64]{ + { + Name: "error", + Data: []byte{def.Int8}, + Error: io.EOF, + ReadCount: 1, + MethodAs: method, + }, + { + Name: "ok", + Data: []byte{def.Int8, 1}, + Expected: 1, + ReadCount: 2, + MethodAs: method, + }, + } + + for _, tc := range testcases { + tc.Run(t) + } +} + +func Test_asIntWithCode(t *testing.T) { + method := func(d *decoder) func(byte, reflect.Kind) (int64, error) { + return d.asIntWithCode + } + testcases := AsXXXTestCases[int64]{ + { + Name: "Uint8.error", + Code: def.Uint8, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Uint8.ok", + Code: def.Uint8, + Data: []byte{1}, + Expected: 1, + ReadCount: 1, + MethodAsWithCode: method, + }, + { + Name: "Int8.error", + Code: def.Int8, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Int8.ok", + Code: def.Int8, + Data: []byte{1}, + Expected: 1, + ReadCount: 1, + MethodAsWithCode: method, + }, + { + Name: "Uint16.error", + Code: def.Uint16, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Uint16.ok", + Code: def.Uint16, + Data: []byte{0, 1}, + Expected: 1, + ReadCount: 1, + MethodAsWithCode: method, + }, + { + Name: "Int16.error", + Code: def.Int16, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Int16.ok", + Code: def.Int16, + Data: []byte{0, 1}, + Expected: 1, + ReadCount: 1, + MethodAsWithCode: method, + }, + { + Name: "Uint32.error", + Code: def.Uint32, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Uint32.ok", + Code: def.Uint32, + Data: []byte{0, 0, 0, 1}, + Expected: 1, + ReadCount: 1, + MethodAsWithCode: method, + }, + { + Name: "Int32.error", + Code: def.Int32, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Int32.ok", + Code: def.Int32, + Data: []byte{0, 0, 0, 1}, + Expected: 1, + ReadCount: 1, + MethodAsWithCode: method, + }, + { + Name: "Uint64.error", + Code: def.Uint64, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Uint64.ok", + Code: def.Uint64, + Data: []byte{0, 0, 0, 0, 0, 0, 0, 1}, + Expected: 1, + ReadCount: 1, + MethodAsWithCode: method, + }, + { + Name: "Int64.error", + Code: def.Int64, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Int64.ok", + Code: def.Int64, + Data: []byte{0, 0, 0, 0, 0, 0, 0, 1}, + Expected: 1, + ReadCount: 1, + MethodAsWithCode: method, + }, + { + Name: "Float32.error", + Code: def.Float32, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Float32.ok", + Code: def.Float32, + Data: []byte{63, 128, 0, 0}, + Expected: 1, + ReadCount: 1, + MethodAsWithCode: method, + }, + { + Name: "Float64.error", + Code: def.Float64, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Float64.ok", + Code: def.Float64, + Data: []byte{63, 240, 0, 0, 0, 0, 0, 0}, + Expected: 1, + ReadCount: 1, + MethodAsWithCode: method, + }, + { + Name: "Nil", + Code: def.Nil, + Expected: 0, + MethodAsWithCode: method, + }, + { + Name: "Unexpected", + Code: def.Array16, + Error: ErrCanNotDecode, + MethodAsWithCode: method, + }, + } + for _, tc := range testcases { + tc.Run(t) + } +} diff --git a/internal/stream/decoding/struct_test.go b/internal/stream/decoding/struct_test.go index f54b4b6..cdc6278 100644 --- a/internal/stream/decoding/struct_test.go +++ b/internal/stream/decoding/struct_test.go @@ -95,8 +95,9 @@ func Test_setStructFromMap(t *testing.T) { { Name: "error.jump", Code: def.Map16, - Data: []byte{0, 1, def.FixStr + 1, 'a', 0xc1}, - ReadCount: 4, + Data: []byte{0, 2, def.FixStr + 1, 'v', 0, def.FixStr + 1, 'b'}, + ReadCount: 6, + Error: io.EOF, MethodAsWithCode: method, }, { @@ -157,8 +158,9 @@ func Test_setStructFromArray(t *testing.T) { { Name: "error.jump", Code: def.Array16, - Data: []byte{0, 2, 0, 0xc1}, - ReadCount: 3, + Data: []byte{0, 2, 0}, + ReadCount: 2, + Error: io.EOF, MethodAsWithCode: method, }, { From ed51f6b6d2f94a2be45c26606707eb90416a6c30 Mon Sep 17 00:00:00 2001 From: shamaton Date: Thu, 8 Aug 2024 08:09:54 +0900 Subject: [PATCH 15/41] fix some tests --- internal/stream/decoding/bool_test.go | 9 ++++- internal/stream/decoding/int_test.go | 2 +- internal/stream/decoding/map_test.go | 21 +++++++++-- internal/stream/decoding/string_test.go | 47 ++++++++++++++++++++++--- 4 files changed, 70 insertions(+), 9 deletions(-) diff --git a/internal/stream/decoding/bool_test.go b/internal/stream/decoding/bool_test.go index 186e99a..c06d709 100644 --- a/internal/stream/decoding/bool_test.go +++ b/internal/stream/decoding/bool_test.go @@ -1,6 +1,7 @@ package decoding import ( + "io" "reflect" "testing" @@ -13,7 +14,13 @@ func Test_asBool(t *testing.T) { } testcases := AsXXXTestCases[bool]{ { - Name: "Bool", + Name: "error", + ReadCount: 0, + Error: io.EOF, + MethodAs: method, + }, + { + Name: "ok", Data: []byte{def.True}, Expected: true, ReadCount: 1, diff --git a/internal/stream/decoding/int_test.go b/internal/stream/decoding/int_test.go index 5a45e5a..76c56ea 100644 --- a/internal/stream/decoding/int_test.go +++ b/internal/stream/decoding/int_test.go @@ -15,7 +15,7 @@ func Test_asInt(t *testing.T) { testcases := AsXXXTestCases[int64]{ { Name: "error", - Data: []byte{def.Int8}, + Data: []byte{}, Error: io.EOF, ReadCount: 1, MethodAs: method, diff --git a/internal/stream/decoding/map_test.go b/internal/stream/decoding/map_test.go index f977f9c..ba36498 100644 --- a/internal/stream/decoding/map_test.go +++ b/internal/stream/decoding/map_test.go @@ -3,6 +3,7 @@ package decoding import ( "bytes" "fmt" + "io" "math" "reflect" "testing" @@ -25,7 +26,15 @@ func Test_mapLength(t *testing.T) { MethodAsWithCode: method, }, { - Name: "Map16", + Name: "Map16.error", + Code: def.Map16, + Data: []byte{}, + ReadCount: 0, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Map16.ok", Code: def.Map16, Data: []byte{0xff, 0xff}, Expected: math.MaxUint16, @@ -33,7 +42,15 @@ func Test_mapLength(t *testing.T) { MethodAsWithCode: method, }, { - Name: "Map32", + Name: "Map32.error", + Code: def.Map32, + Data: []byte{}, + ReadCount: 0, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Map32.ok", Code: def.Map32, Data: []byte{0xff, 0xff, 0xff, 0xff}, Expected: math.MaxUint32, diff --git a/internal/stream/decoding/string_test.go b/internal/stream/decoding/string_test.go index db33cfc..d44e1ca 100644 --- a/internal/stream/decoding/string_test.go +++ b/internal/stream/decoding/string_test.go @@ -21,7 +21,15 @@ func Test_stringByteLength(t *testing.T) { MethodAsWithCode: method, }, { - Name: "Str8", + Name: "Str8.error", + Code: def.Str8, + Data: []byte{}, + Error: io.EOF, + ReadCount: 0, + MethodAsWithCode: method, + }, + { + Name: "Str8.ok", Code: def.Str8, Data: []byte{0xff}, Expected: math.MaxUint8, @@ -29,7 +37,15 @@ func Test_stringByteLength(t *testing.T) { MethodAsWithCode: method, }, { - Name: "Str16", + Name: "Str16.error", + Code: def.Str16, + Data: []byte{}, + Error: io.EOF, + ReadCount: 0, + MethodAsWithCode: method, + }, + { + Name: "Str16.ok", Code: def.Str16, Data: []byte{0xff, 0xff}, Expected: math.MaxUint16, @@ -37,7 +53,15 @@ func Test_stringByteLength(t *testing.T) { MethodAsWithCode: method, }, { - Name: "Str32", + Name: "Str32.error", + Code: def.Str32, + Data: []byte{}, + Error: io.EOF, + ReadCount: 0, + MethodAsWithCode: method, + }, + { + Name: "Str32.ok", Code: def.Str32, Data: []byte{0xff, 0xff, 0xff, 0xff}, Expected: math.MaxUint32, @@ -50,6 +74,12 @@ func Test_stringByteLength(t *testing.T) { Expected: 0, MethodAsWithCode: method, }, + { + Name: "Unexpected", + Code: def.Array16, + Error: ErrCanNotDecode, + MethodAsWithCode: method, + }, } for _, tc := range testcases { @@ -63,14 +93,21 @@ func Test_asString(t *testing.T) { } testcases := AsXXXTestCases[string]{ { - Name: "String.error", + Name: "error.code", + Data: []byte{}, + Error: io.EOF, + ReadCount: 0, + MethodAs: method, + }, + { + Name: "error.string", Data: []byte{def.FixStr + 1}, Error: io.EOF, ReadCount: 1, MethodAs: method, }, { - Name: "String.ok", + Name: "ok", Data: []byte{def.FixStr + 1, 'a'}, Expected: "a", ReadCount: 2, From 57e52104a7c06206102f9d526122d2b65d279942 Mon Sep 17 00:00:00 2001 From: shamaton Date: Thu, 8 Aug 2024 08:13:00 +0900 Subject: [PATCH 16/41] fix decoding int tests --- internal/stream/decoding/int_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/stream/decoding/int_test.go b/internal/stream/decoding/int_test.go index 76c56ea..6224f52 100644 --- a/internal/stream/decoding/int_test.go +++ b/internal/stream/decoding/int_test.go @@ -17,7 +17,7 @@ func Test_asInt(t *testing.T) { Name: "error", Data: []byte{}, Error: io.EOF, - ReadCount: 1, + ReadCount: 0, MethodAs: method, }, { From 5dc8e94e14f23e6b104af5e2de03de26abb206b5 Mon Sep 17 00:00:00 2001 From: shamaton Date: Thu, 8 Aug 2024 22:31:00 +0900 Subject: [PATCH 17/41] add decoding slice tests --- internal/common/testutil/testutil.go | 4 +- internal/stream/decoding/map_test.go | 16 +-- internal/stream/decoding/slice_test.go | 131 +++++++++++++++++++++++++ 3 files changed, 135 insertions(+), 16 deletions(-) create mode 100644 internal/stream/decoding/slice_test.go diff --git a/internal/common/testutil/testutil.go b/internal/common/testutil/testutil.go index 9420e23..d4054b1 100644 --- a/internal/common/testutil/testutil.go +++ b/internal/common/testutil/testutil.go @@ -51,7 +51,9 @@ func EqualSlice[T comparable](t *testing.T, actual, expected []T) { t.Fatalf("diffrent length. actual: %v, expected: %v", actual, expected) } for i := range actual { - Equal[T](t, actual[i], expected[i]) + if !reflect.DeepEqual(actual[i], expected[i]) { + t.Fatalf("not equal. actual: %v, expected: %v", actual, expected) + } } } diff --git a/internal/stream/decoding/map_test.go b/internal/stream/decoding/map_test.go index ba36498..5254d2c 100644 --- a/internal/stream/decoding/map_test.go +++ b/internal/stream/decoding/map_test.go @@ -1,17 +1,14 @@ package decoding import ( - "bytes" "fmt" "io" "math" "reflect" "testing" - tu "github.com/shamaton/msgpack/v2/internal/common/testutil" - "github.com/shamaton/msgpack/v2/internal/stream/encoding" - "github.com/shamaton/msgpack/v2/def" + tu "github.com/shamaton/msgpack/v2/internal/common/testutil" ) func Test_mapLength(t *testing.T) { @@ -70,17 +67,6 @@ func Test_mapLength(t *testing.T) { } } -func TestHoge(t *testing.T) { - buf := new(bytes.Buffer) - err := encoding.Encode(buf, map[string]int{"a": 1}, false) - tu.NoError(t, err) - - v := map[string]int{} - err = Decode(buf, &v, false) - tu.NoError(t, err) - t.Log(v) -} - func Test_asFixedMap_StringInt(t *testing.T) { run := func(t *testing.T, v any, dv byte) { method := func(d *decoder) (bool, error) { diff --git a/internal/stream/decoding/slice_test.go b/internal/stream/decoding/slice_test.go new file mode 100644 index 0000000..4bd4464 --- /dev/null +++ b/internal/stream/decoding/slice_test.go @@ -0,0 +1,131 @@ +package decoding + +import ( + "io" + "math" + "reflect" + "testing" + + "github.com/shamaton/msgpack/v2/def" + tu "github.com/shamaton/msgpack/v2/internal/common/testutil" +) + +func Test_sliceLength(t *testing.T) { + method := func(d *decoder) func(byte, reflect.Kind) (int, error) { + return d.sliceLength + } + testcases := AsXXXTestCases[int]{ + { + Name: "FixArray", + Code: def.FixArray + 3, + Expected: 3, + MethodAsWithCode: method, + }, + { + Name: "Array16.error", + Code: def.Array16, + Data: []byte{}, + ReadCount: 0, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Array16.ok", + Code: def.Array16, + Data: []byte{0xff, 0xff}, + Expected: math.MaxUint16, + ReadCount: 1, + MethodAsWithCode: method, + }, + { + Name: "Array32.error", + Code: def.Array32, + Data: []byte{}, + ReadCount: 0, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "Array32.ok", + Code: def.Array32, + Data: []byte{0xff, 0xff, 0xff, 0xff}, + Expected: math.MaxUint32, + ReadCount: 1, + MethodAsWithCode: method, + }, + { + Name: "Unexpected", + Code: def.Nil, + Error: ErrCanNotDecode, + MethodAsWithCode: method, + }, + } + + for _, tc := range testcases { + tc.Run(t) + } +} + +func Test_asFixedSlice_Int(t *testing.T) { + run := func(t *testing.T, v any) { + method := func(d *decoder) (bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedSlice(rv.Elem(), 1) + } + + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{}, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "ok", + Data: []byte{def.PositiveFixIntMin + 3}, + Expected: true, + ReadCount: 1, + MethodAsCustom: method, + }, + } + for _, tc := range testcases { + tc.Run(t) + } + } + + v1 := new([]int) + run(t, v1) + tu.EqualSlice(t, *v1, []int{3}) +} + +func Test_asFixedSlice_Uint(t *testing.T) { + run := func(t *testing.T, v any) { + method := func(d *decoder) (bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedSlice(rv.Elem(), 1) + } + + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{}, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "ok", + Data: []byte{def.PositiveFixIntMin + 5}, + Expected: true, + ReadCount: 1, + MethodAsCustom: method, + }, + } + for _, tc := range testcases { + tc.Run(t) + } + } + + v1 := new([]uint) + run(t, v1) + tu.EqualSlice(t, *v1, []uint{5}) +} From 62005b8cb4882f08fa028d2c4068a8d17bfb1e8c Mon Sep 17 00:00:00 2001 From: shamaton Date: Thu, 8 Aug 2024 22:48:49 +0900 Subject: [PATCH 18/41] add more decoding slice tests --- internal/stream/decoding/slice_test.go | 384 +++++++++++++++++++++++++ 1 file changed, 384 insertions(+) diff --git a/internal/stream/decoding/slice_test.go b/internal/stream/decoding/slice_test.go index 4bd4464..5de78f8 100644 --- a/internal/stream/decoding/slice_test.go +++ b/internal/stream/decoding/slice_test.go @@ -98,6 +98,134 @@ func Test_asFixedSlice_Int(t *testing.T) { tu.EqualSlice(t, *v1, []int{3}) } +func Test_asFixedSlice_Int8(t *testing.T) { + run := func(t *testing.T, v any) { + method := func(d *decoder) (bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedSlice(rv.Elem(), 1) + } + + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{}, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "ok", + Data: []byte{def.PositiveFixIntMin + 4}, + Expected: true, + ReadCount: 1, + MethodAsCustom: method, + }, + } + for _, tc := range testcases { + tc.Run(t) + } + } + + v1 := new([]int8) + run(t, v1) + tu.EqualSlice(t, *v1, []int8{4}) +} + +func Test_asFixedSlice_Int16(t *testing.T) { + run := func(t *testing.T, v any) { + method := func(d *decoder) (bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedSlice(rv.Elem(), 1) + } + + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{}, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "ok", + Data: []byte{def.PositiveFixIntMin + 5}, + Expected: true, + ReadCount: 1, + MethodAsCustom: method, + }, + } + for _, tc := range testcases { + tc.Run(t) + } + } + + v1 := new([]int16) + run(t, v1) + tu.EqualSlice(t, *v1, []int16{5}) +} + +func Test_asFixedSlice_Int32(t *testing.T) { + run := func(t *testing.T, v any) { + method := func(d *decoder) (bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedSlice(rv.Elem(), 1) + } + + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{}, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "ok", + Data: []byte{def.PositiveFixIntMin + 6}, + Expected: true, + ReadCount: 1, + MethodAsCustom: method, + }, + } + for _, tc := range testcases { + tc.Run(t) + } + } + + v1 := new([]int32) + run(t, v1) + tu.EqualSlice(t, *v1, []int32{6}) +} + +func Test_asFixedSlice_Int64(t *testing.T) { + run := func(t *testing.T, v any) { + method := func(d *decoder) (bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedSlice(rv.Elem(), 1) + } + + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{}, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "ok", + Data: []byte{def.PositiveFixIntMin + 7}, + Expected: true, + ReadCount: 1, + MethodAsCustom: method, + }, + } + for _, tc := range testcases { + tc.Run(t) + } + } + + v1 := new([]int64) + run(t, v1) + tu.EqualSlice(t, *v1, []int64{7}) +} + func Test_asFixedSlice_Uint(t *testing.T) { run := func(t *testing.T, v any) { method := func(d *decoder) (bool, error) { @@ -129,3 +257,259 @@ func Test_asFixedSlice_Uint(t *testing.T) { run(t, v1) tu.EqualSlice(t, *v1, []uint{5}) } + +func Test_asFixedSlice_Uint8(t *testing.T) { + run := func(t *testing.T, v any) { + method := func(d *decoder) (bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedSlice(rv.Elem(), 1) + } + + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{}, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "ok", + Data: []byte{def.PositiveFixIntMin + 6}, + Expected: true, + ReadCount: 1, + MethodAsCustom: method, + }, + } + for _, tc := range testcases { + tc.Run(t) + } + } + + v1 := new([]uint8) + run(t, v1) + tu.EqualSlice(t, *v1, []uint8{6}) +} + +func Test_asFixedSlice_Uint16(t *testing.T) { + run := func(t *testing.T, v any) { + method := func(d *decoder) (bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedSlice(rv.Elem(), 1) + } + + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{}, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "ok", + Data: []byte{def.PositiveFixIntMin + 7}, + Expected: true, + ReadCount: 1, + MethodAsCustom: method, + }, + } + for _, tc := range testcases { + tc.Run(t) + } + } + + v1 := new([]uint16) + run(t, v1) + tu.EqualSlice(t, *v1, []uint16{7}) +} + +func Test_asFixedSlice_Uint32(t *testing.T) { + run := func(t *testing.T, v any) { + method := func(d *decoder) (bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedSlice(rv.Elem(), 1) + } + + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{}, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "ok", + Data: []byte{def.PositiveFixIntMin + 8}, + Expected: true, + ReadCount: 1, + MethodAsCustom: method, + }, + } + for _, tc := range testcases { + tc.Run(t) + } + } + + v1 := new([]uint32) + run(t, v1) + tu.EqualSlice(t, *v1, []uint32{8}) +} + +func Test_asFixedSlice_Uint64(t *testing.T) { + run := func(t *testing.T, v any) { + method := func(d *decoder) (bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedSlice(rv.Elem(), 1) + } + + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{}, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "ok", + Data: []byte{def.PositiveFixIntMin + 9}, + Expected: true, + ReadCount: 1, + MethodAsCustom: method, + }, + } + for _, tc := range testcases { + tc.Run(t) + } + } + + v1 := new([]uint64) + run(t, v1) + tu.EqualSlice(t, *v1, []uint64{9}) +} + +func Test_asFixedSlice_Float32(t *testing.T) { + run := func(t *testing.T, v any) { + method := func(d *decoder) (bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedSlice(rv.Elem(), 1) + } + + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{}, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "ok", + Data: []byte{def.Float32, 63, 128, 0, 0}, + Expected: true, + ReadCount: 2, + MethodAsCustom: method, + }, + } + for _, tc := range testcases { + tc.Run(t) + } + } + + v1 := new([]float32) + run(t, v1) + tu.EqualSlice(t, *v1, []float32{1}) +} + +func Test_asFixedSlice_Float64(t *testing.T) { + run := func(t *testing.T, v any) { + method := func(d *decoder) (bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedSlice(rv.Elem(), 1) + } + + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{}, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "ok", + Data: []byte{def.Float64, 63, 240, 0, 0, 0, 0, 0, 0}, + Expected: true, + ReadCount: 2, + MethodAsCustom: method, + }, + } + for _, tc := range testcases { + tc.Run(t) + } + } + + v1 := new([]float64) + run(t, v1) + tu.EqualSlice(t, *v1, []float64{1}) +} + +func Test_asFixedSlice_String(t *testing.T) { + run := func(t *testing.T, v any) { + method := func(d *decoder) (bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedSlice(rv.Elem(), 2) + } + + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{}, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "ok", + Data: []byte{def.FixStr + 1, 'a', def.FixStr + 1, 'b'}, + Expected: true, + ReadCount: 4, + MethodAsCustom: method, + }, + } + for _, tc := range testcases { + tc.Run(t) + } + } + + v1 := new([]string) + run(t, v1) + tu.EqualSlice(t, *v1, []string{"a", "b"}) +} + +func Test_asFixedSlice_Bool(t *testing.T) { + run := func(t *testing.T, v any) { + method := func(d *decoder) (bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedSlice(rv.Elem(), 1) + } + + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{}, + Error: io.EOF, + MethodAsCustom: method, + }, + { + Name: "ok", + Data: []byte{def.True}, + Expected: true, + ReadCount: 1, + MethodAsCustom: method, + }, + } + for _, tc := range testcases { + tc.Run(t) + } + } + + v1 := new([]bool) + run(t, v1) + tu.EqualSlice(t, *v1, []bool{true}) +} From 041830c92ded2f7e0998c24863c82ae105cd497b Mon Sep 17 00:00:00 2001 From: shamaton Date: Sat, 10 Aug 2024 00:01:00 +0900 Subject: [PATCH 19/41] add decoding tests --- internal/stream/decoding/decoding_test.go | 369 ++++++++++++++++++++++ 1 file changed, 369 insertions(+) diff --git a/internal/stream/decoding/decoding_test.go b/internal/stream/decoding/decoding_test.go index 2429cca..d6cee50 100644 --- a/internal/stream/decoding/decoding_test.go +++ b/internal/stream/decoding/decoding_test.go @@ -6,6 +6,8 @@ import ( "reflect" "testing" + "github.com/shamaton/msgpack/v2/def" + "github.com/shamaton/msgpack/v2/internal/common" tu "github.com/shamaton/msgpack/v2/internal/common/testutil" ) @@ -25,6 +27,12 @@ type AsXXXTestCase[T any] struct { type AsXXXTestCases[T any] []AsXXXTestCase[T] +func (tcs AsXXXTestCases[T]) Run(t *testing.T) { + for _, tc := range tcs { + tc.Run(t) + } +} + func (tc *AsXXXTestCase[T]) Run(t *testing.T) { const kind = reflect.String t.Helper() @@ -74,3 +82,364 @@ func (tc *AsXXXTestCase[T]) Run(t *testing.T) { tu.Equal(t, n, 0) }) } + +func TestDecoding(t *testing.T) { + t.Run("nil reader", func(t *testing.T) { + v := new(int) + err := Decode(nil, v, false) + tu.Error(t, err) + tu.Equal(t, err.Error(), "reader is nil") + }) +} + +func Test_decodeWithCode(t *testing.T) { + + var target any + method := func(d *decoder) func(code byte, _ reflect.Kind) (bool, error) { + return func(code byte, _ reflect.Kind) (bool, error) { + rv := reflect.ValueOf(target) + return true, d.decodeWithCode(code, rv.Elem()) + } + } + + t.Run("Int", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Code: def.Int8, + Data: []byte{}, + ReadCount: 0, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "ok", + Code: def.Int8, + Data: []byte{5}, + Expected: true, + ReadCount: 1, + MethodAsWithCode: method, + }, + } + v := new(int) + target = v + testcases.Run(t) + tu.Equal(t, *v, 5) + }) + t.Run("Uint", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Code: def.Uint8, + Data: []byte{}, + ReadCount: 0, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "ok", + Code: def.Uint8, + Data: []byte{5}, + Expected: true, + ReadCount: 1, + MethodAsWithCode: method, + }, + } + v := new(uint) + target = v + testcases.Run(t) + tu.Equal(t, *v, 5) + }) + t.Run("Float32", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Code: def.Float32, + Data: []byte{}, + ReadCount: 0, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "ok", + Code: def.Float32, + Data: []byte{63, 128, 0, 0}, + Expected: true, + ReadCount: 1, + MethodAsWithCode: method, + }, + } + v := new(float32) + target = v + testcases.Run(t) + tu.Equal(t, *v, 1) + }) + t.Run("Float64", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Code: def.Float64, + Data: []byte{}, + ReadCount: 0, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "ok", + Code: def.Float64, + Data: []byte{63, 240, 0, 0, 0, 0, 0, 0}, + Expected: true, + ReadCount: 1, + MethodAsWithCode: method, + }, + } + v := new(float64) + target = v + testcases.Run(t) + tu.Equal(t, *v, 1) + }) + t.Run("BinString", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Code: def.Bin8, + Data: []byte{}, + ReadCount: 0, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "ok", + Code: def.Bin8, + Data: []byte{1, 'a'}, + Expected: true, + ReadCount: 2, + MethodAsWithCode: method, + }, + } + v := new(string) + target = v + testcases.Run(t) + tu.Equal(t, *v, "a") + }) + t.Run("String", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Code: def.Str8, + Data: []byte{}, + Expected: false, + ReadCount: 0, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "ok", + Code: def.Str8, + Data: []byte{1, 'b'}, + Expected: true, + ReadCount: 2, + MethodAsWithCode: method, + }, + } + v := new(string) + target = v + testcases.Run(t) + tu.Equal(t, *v, "b") + }) + t.Run("Bool", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Code: def.Int8, + Data: []byte{}, + ReadCount: 0, + Error: ErrCanNotDecode, + MethodAsWithCode: method, + }, + { + Name: "ok", + Code: def.True, + Data: []byte{}, + Expected: true, + ReadCount: 0, + MethodAsWithCode: method, + }, + } + v := new(bool) + target = v + testcases.Run(t) + tu.Equal(t, *v, true) + }) + t.Run("Slice.nil", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "ok", + Code: def.Nil, + Data: []byte{}, + Expected: true, + ReadCount: 0, + MethodAsWithCode: method, + }, + } + v := new([]int) + target = v + testcases.Run(t) + tu.Equal(t, *v, nil) + }) + t.Run("Slice.bin", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Code: def.Bin8, + Data: []byte{}, + ReadCount: 0, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "ok", + Code: def.Bin8, + Data: []byte{1, 2}, + Expected: true, + ReadCount: 2, + MethodAsWithCode: method, + }, + } + v := new([]byte) + target = v + testcases.Run(t) + tu.Equal(t, *v, []byte{2}) + }) + t.Run("Slice.string", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error.strlen", + Code: def.Str8, + Data: []byte{}, + ReadCount: 0, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "error.bytelen", + Code: def.Str8, + Data: []byte{1}, + ReadCount: 1, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "ok", + Code: def.Str8, + Data: []byte{1, 'c'}, + Expected: true, + ReadCount: 2, + MethodAsWithCode: method, + }, + } + v := new([]byte) + target = v + testcases.Run(t) + tu.Equal(t, *v, []byte{'c'}) + }) + t.Run("Slice.fixed", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error.strlen", + Code: def.Array16, + Data: []byte{}, + ReadCount: 0, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "error.slice", + Code: def.Array16, + Data: []byte{0, 1}, + ReadCount: 1, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "ok", + Code: def.Array16, + Data: []byte{0, 1, def.PositiveFixIntMin + 3}, + Expected: true, + ReadCount: 2, + MethodAsWithCode: method, + }, + } + v := new([]int) + target = v + testcases.Run(t) + tu.Equal(t, *v, []int{3}) + }) + t.Run("Slice.struct", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error.strlen", + Code: def.Array16, + Data: []byte{}, + ReadCount: 0, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "error.slice", + Code: def.Array16, + Data: []byte{0, 1}, + ReadCount: 1, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "ok", + Code: def.Array16, + Data: []byte{0, 1, def.FixMap + 1, def.FixStr + 1, 'v', def.PositiveFixIntMin + 3}, + Expected: true, + ReadCount: 5, + MethodAsWithCode: method, + }, + } + type st struct { + V int `msgpack:"v"` + } + v := new([]st) + target = v + testcases.Run(t) + tu.Equal(t, *v, []st{{V: 3}}) + }) + t.Run("Slice.map", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error.strlen", + Code: def.Array16, + Data: []byte{}, + ReadCount: 0, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "error.slice", + Code: def.Array16, + Data: []byte{0, 1}, + ReadCount: 1, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "ok", + Code: def.Array16, + Data: []byte{0, 1, def.FixMap + 1, def.FixStr + 1, 'v', def.PositiveFixIntMin + 3}, + Expected: true, + ReadCount: 5, + MethodAsWithCode: method, + }, + } + v := new([]map[string]int) + target = v + testcases.Run(t) + tu.Equal(t, *v, []map[string]int{{"v": 3}}) + }) +} From 795043d7d43fae2a90873f7eef52fa97b0887cc6 Mon Sep 17 00:00:00 2001 From: shamaton Date: Sat, 10 Aug 2024 01:38:49 +0900 Subject: [PATCH 20/41] add more decoding tests --- internal/stream/decoding/decoding.go | 8 +- internal/stream/decoding/decoding_test.go | 417 ++++++++++++++++++++++ 2 files changed, 422 insertions(+), 3 deletions(-) diff --git a/internal/stream/decoding/decoding.go b/internal/stream/decoding/decoding.go index cd220af..d849bf8 100644 --- a/internal/stream/decoding/decoding.go +++ b/internal/stream/decoding/decoding.go @@ -190,7 +190,7 @@ func (d *decoder) decodeWithCode(code byte, rv reflect.Value) error { return err } if len(bs) > rv.Len() { - return fmt.Errorf("%v len is %d, but msgpack has %d elements", rv.Type(), rv.Len(), len(bs)) + return fmt.Errorf("%v len is %d, but msgpack has %d elements, %w", rv.Type(), rv.Len(), len(bs), ErrNotMatchArrayElement) } for i, b := range bs { rv.Index(i).SetUint(uint64(b)) @@ -204,7 +204,7 @@ func (d *decoder) decodeWithCode(code byte, rv reflect.Value) error { return err } if l > rv.Len() { - return fmt.Errorf("%v len is %d, but msgpack has %d elements", rv.Type(), rv.Len(), l) + return fmt.Errorf("%v len is %d, but msgpack has %d elements, %w", rv.Type(), rv.Len(), l, ErrNotMatchArrayElement) } bs, err := d.asStringByteByLength(l, k) if err != nil { @@ -223,7 +223,7 @@ func (d *decoder) decodeWithCode(code byte, rv reflect.Value) error { } if l > rv.Len() { - return fmt.Errorf("%v len is %d, but msgpack has %d elements", rv.Type(), rv.Len(), l) + return fmt.Errorf("%v len is %d, but msgpack has %d elements, %w", rv.Type(), rv.Len(), l, ErrNotMatchArrayElement) } // create array dynamically @@ -320,6 +320,8 @@ func (d *decoder) decodeWithCode(code byte, rv reflect.Value) error { return nil } +var ErrNotMatchArrayElement = errors.New("not match array element") + var ErrCanNotDecode = errors.New("msgpack : invalid code") func (d *decoder) errorTemplate(code byte, k reflect.Kind) error { diff --git a/internal/stream/decoding/decoding_test.go b/internal/stream/decoding/decoding_test.go index d6cee50..4837998 100644 --- a/internal/stream/decoding/decoding_test.go +++ b/internal/stream/decoding/decoding_test.go @@ -393,6 +393,14 @@ func Test_decodeWithCode(t *testing.T) { Error: io.EOF, MethodAsWithCode: method, }, + { + Name: "error.struct", + Code: def.Array16, + Data: []byte{0, 1, def.FixMap + 1, def.FixStr + 1, 'v'}, + ReadCount: 4, + Error: io.EOF, + MethodAsWithCode: method, + }, { Name: "ok", Code: def.Array16, @@ -428,6 +436,14 @@ func Test_decodeWithCode(t *testing.T) { Error: io.EOF, MethodAsWithCode: method, }, + { + Name: "error.map", + Code: def.Array16, + Data: []byte{0, 1, def.FixMap + 1, def.FixStr + 1, 'v'}, + ReadCount: 4, + Error: io.EOF, + MethodAsWithCode: method, + }, { Name: "ok", Code: def.Array16, @@ -442,4 +458,405 @@ func Test_decodeWithCode(t *testing.T) { testcases.Run(t) tu.Equal(t, *v, []map[string]int{{"v": 3}}) }) + t.Run("Complex64", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Code: def.Fixext8, + Data: []byte{}, + ReadCount: 0, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "ok", + Code: def.Fixext8, + Data: []byte{byte(def.ComplexTypeCode()), 63, 128, 0, 0, 63, 128, 0, 0}, + Expected: true, + ReadCount: 3, + MethodAsWithCode: method, + }, + } + v := new(complex64) + target = v + testcases.Run(t) + tu.Equal(t, *v, complex(1, 1)) + }) + t.Run("Complex128", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Code: def.Fixext8, + Data: []byte{}, + ReadCount: 0, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "ok", + Code: def.Fixext8, + Data: []byte{byte(def.ComplexTypeCode()), 63, 128, 0, 0, 63, 128, 0, 0}, + Expected: true, + ReadCount: 3, + MethodAsWithCode: method, + }, + } + v := new(complex128) + target = v + testcases.Run(t) + tu.Equal(t, *v, complex(1, 1)) + }) + + t.Run("Array.nil", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "ok", + Code: def.Nil, + Data: []byte{}, + Expected: true, + ReadCount: 0, + MethodAsWithCode: method, + }, + } + v := new([1]int) + target = v + testcases.Run(t) + tu.Equal(t, *v, [1]int{}) + }) + t.Run("Array.bin", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error.bin", + Code: def.Bin8, + Data: []byte{}, + ReadCount: 0, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "error.len", + Code: def.Bin8, + Data: []byte{2, 1, 2}, + ReadCount: 2, + Error: ErrNotMatchArrayElement, + MethodAsWithCode: method, + }, + { + Name: "ok", + Code: def.Bin8, + Data: []byte{1, 2}, + Expected: true, + ReadCount: 2, + MethodAsWithCode: method, + }, + } + v := new([1]byte) + target = v + testcases.Run(t) + tu.Equal(t, *v, [1]byte{2}) + }) + t.Run("Array.string", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error.strlen", + Code: def.Str8, + Data: []byte{}, + ReadCount: 0, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "error.compare", + Code: def.Str8, + Data: []byte{2}, + ReadCount: 1, + Error: ErrNotMatchArrayElement, + MethodAsWithCode: method, + }, + { + Name: "error.bytelen", + Code: def.Str8, + Data: []byte{1}, + ReadCount: 1, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "ok", + Code: def.Str8, + Data: []byte{1, 'c'}, + Expected: true, + ReadCount: 2, + MethodAsWithCode: method, + }, + } + v := new([1]byte) + target = v + testcases.Run(t) + tu.Equal(t, *v, [1]byte{'c'}) + }) + t.Run("Array.struct", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error.strlen", + Code: def.Array16, + Data: []byte{}, + ReadCount: 0, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "error.strlen", + Code: def.Array16, + Data: []byte{0, 2}, + ReadCount: 1, + Error: ErrNotMatchArrayElement, + MethodAsWithCode: method, + }, + { + Name: "error.slice", + Code: def.Array16, + Data: []byte{0, 1}, + ReadCount: 1, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "error.struct", + Code: def.Array16, + Data: []byte{0, 1, def.FixMap + 1, def.FixStr + 1, 'v'}, + ReadCount: 4, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "ok", + Code: def.Array16, + Data: []byte{0, 1, def.FixMap + 1, def.FixStr + 1, 'v', def.PositiveFixIntMin + 3}, + Expected: true, + ReadCount: 5, + MethodAsWithCode: method, + }, + } + type st struct { + V int `msgpack:"v"` + } + v := new([1]st) + target = v + testcases.Run(t) + tu.Equal(t, *v, [1]st{{V: 3}}) + }) + t.Run("Map.nil", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "ok", + Code: def.Nil, + Data: []byte{}, + Expected: true, + ReadCount: 0, + MethodAsWithCode: method, + }, + } + v := new([]map[string]int) + target = v + testcases.Run(t) + tu.Equal(t, *v, nil) + }) + t.Run("Map.fixed", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error.strlen", + Code: def.Map16, + Data: []byte{}, + ReadCount: 0, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "error.map", + Code: def.Map16, + Data: []byte{0, 1}, + ReadCount: 1, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "ok", + Code: def.Map16, + Data: []byte{0, 1, def.FixStr + 1, 'a', def.PositiveFixIntMin + 3}, + Expected: true, + ReadCount: 4, + MethodAsWithCode: method, + }, + } + v := new(map[string]int) + target = v + testcases.Run(t) + tu.Equal(t, *v, map[string]int{"a": 3}) + }) + t.Run("Map.struct", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error.strlen", + Code: def.Map16, + Data: []byte{}, + ReadCount: 0, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "error.key", + Code: def.Map16, + Data: []byte{0, 1}, + ReadCount: 1, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "error.value", + Code: def.Map16, + Data: []byte{0, 1, def.FixStr + 1, 'a', def.FixMap + 1, def.FixStr + 1, 'v'}, + Expected: true, + ReadCount: 6, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "ok", + Code: def.Map16, + Data: []byte{0, 1, def.FixStr + 1, 'a', def.FixMap + 1, def.FixStr + 1, 'v', def.PositiveFixIntMin + 3}, + Expected: true, + ReadCount: 7, + MethodAsWithCode: method, + }, + } + type st struct { + V int `msgpack:"v"` + } + v := new(map[string]st) + target = v + testcases.Run(t) + tu.Equal(t, *v, map[string]st{"a": {V: 3}}) + }) + t.Run("Struct", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Code: def.Map16, + Data: []byte{}, + ReadCount: 0, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "ok", + Code: def.Map16, + Data: []byte{0, 1, def.FixStr + 1, 'v', def.PositiveFixIntMin + 3}, + Expected: true, + ReadCount: 4, + MethodAsWithCode: method, + }, + } + type st struct { + V int `msgpack:"v"` + } + v := new(st) + target = v + testcases.Run(t) + tu.Equal(t, *v, st{V: 3}) + }) + t.Run("Ptr.nil", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "ok", + Code: def.Nil, + Data: []byte{}, + Expected: true, + ReadCount: 0, + MethodAsWithCode: method, + }, + } + v := new(int) + target = &v + testcases.Run(t) + tu.Equal(t, *v, 0) + }) + t.Run("Ptr", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Code: def.Int8, + Data: []byte{}, + ReadCount: 0, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "ok", + Code: def.Int8, + Data: []byte{3}, + Expected: true, + ReadCount: 1, + MethodAsWithCode: method, + }, + } + v := new(int) + target = &v + testcases.Run(t) + tu.Equal(t, *v, 3) + }) + t.Run("Interface.ptr", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Code: def.Int8, + Data: []byte{}, + ReadCount: 0, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "ok", + Code: def.Int8, + Data: []byte{3}, + Expected: true, + ReadCount: 1, + MethodAsWithCode: method, + }, + } + var v interface{} + v = new(int) + target = &v + testcases.Run(t) + vv := v.(*int) + tu.Equal(t, *vv, 3) + }) + t.Run("Interface", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Code: def.Map16, + Data: []byte{}, + ReadCount: 0, + Error: io.EOF, + MethodAsWithCode: method, + }, + { + Name: "ok", + Code: def.Map16, + Data: []byte{0, 1, def.FixStr + 1, 'v', def.PositiveFixIntMin + 3}, + Expected: true, + ReadCount: 4, + MethodAsWithCode: method, + }, + } + type st struct { + V any `msgpack:"v"` + } + v := new(st) + target = v + testcases.Run(t) + var vv any = uint8(3) + tu.Equal(t, v.V, vv) + }) } From 597e18f3477224cd26dbb31bd8dfc59587834c40 Mon Sep 17 00:00:00 2001 From: shamaton Date: Sat, 10 Aug 2024 01:57:16 +0900 Subject: [PATCH 21/41] fix decoding tests --- internal/stream/decoding/decoding_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/stream/decoding/decoding_test.go b/internal/stream/decoding/decoding_test.go index 4837998..0ab342a 100644 --- a/internal/stream/decoding/decoding_test.go +++ b/internal/stream/decoding/decoding_test.go @@ -657,7 +657,7 @@ func Test_decodeWithCode(t *testing.T) { MethodAsWithCode: method, }, } - v := new([]map[string]int) + v := new(map[string]int) target = v testcases.Run(t) tu.Equal(t, *v, nil) @@ -836,17 +836,17 @@ func Test_decodeWithCode(t *testing.T) { { Name: "error", Code: def.Map16, - Data: []byte{}, - ReadCount: 0, + Data: []byte{0, 1, def.FixStr + 1, 'v', def.Int8}, + ReadCount: 4, Error: io.EOF, MethodAsWithCode: method, }, { Name: "ok", Code: def.Map16, - Data: []byte{0, 1, def.FixStr + 1, 'v', def.PositiveFixIntMin + 3}, + Data: []byte{0, 1, def.FixStr + 1, 'v', def.Int8, 3}, Expected: true, - ReadCount: 4, + ReadCount: 5, MethodAsWithCode: method, }, } @@ -856,7 +856,7 @@ func Test_decodeWithCode(t *testing.T) { v := new(st) target = v testcases.Run(t) - var vv any = uint8(3) + var vv any = int8(3) tu.Equal(t, v.V, vv) }) } From eac82432f833738fc970cbc037497ad07ff6e386 Mon Sep 17 00:00:00 2001 From: shamaton Date: Sat, 10 Aug 2024 11:50:25 +0900 Subject: [PATCH 22/41] define error valiables in def package --- def/error.go | 10 ++++++++++ internal/stream/decoding/decoding.go | 15 +++++---------- internal/stream/decoding/interface.go | 8 ++------ 3 files changed, 17 insertions(+), 16 deletions(-) create mode 100644 def/error.go diff --git a/def/error.go b/def/error.go new file mode 100644 index 0000000..917512a --- /dev/null +++ b/def/error.go @@ -0,0 +1,10 @@ +package def + +import "errors" + +var ( + ErrNotMatchArrayElement = errors.New("not match array element") + ErrCanNotDecode = errors.New("msgpack : invalid code") + ErrCanNotSetSliceAsMapKey = errors.New("can not set slice as map key") + ErrCanNotSetMapAsMapKey = errors.New("can not set map as map key") +) diff --git a/internal/stream/decoding/decoding.go b/internal/stream/decoding/decoding.go index d849bf8..d86de40 100644 --- a/internal/stream/decoding/decoding.go +++ b/internal/stream/decoding/decoding.go @@ -1,11 +1,11 @@ package decoding import ( - "errors" "fmt" "io" "reflect" + "github.com/shamaton/msgpack/v2/def" "github.com/shamaton/msgpack/v2/internal/common" ) @@ -19,7 +19,6 @@ type decoder struct { // Decode analyzes the MessagePack-encoded data and stores // the result into the pointer of v. func Decode(r io.Reader, v interface{}, asArray bool) error { - if r == nil { return fmt.Errorf("reader is nil") } @@ -190,7 +189,7 @@ func (d *decoder) decodeWithCode(code byte, rv reflect.Value) error { return err } if len(bs) > rv.Len() { - return fmt.Errorf("%v len is %d, but msgpack has %d elements, %w", rv.Type(), rv.Len(), len(bs), ErrNotMatchArrayElement) + return fmt.Errorf("%v len is %d, but msgpack has %d elements, %w", rv.Type(), rv.Len(), len(bs), def.ErrNotMatchArrayElement) } for i, b := range bs { rv.Index(i).SetUint(uint64(b)) @@ -204,7 +203,7 @@ func (d *decoder) decodeWithCode(code byte, rv reflect.Value) error { return err } if l > rv.Len() { - return fmt.Errorf("%v len is %d, but msgpack has %d elements, %w", rv.Type(), rv.Len(), l, ErrNotMatchArrayElement) + return fmt.Errorf("%v len is %d, but msgpack has %d elements, %w", rv.Type(), rv.Len(), l, def.ErrNotMatchArrayElement) } bs, err := d.asStringByteByLength(l, k) if err != nil { @@ -223,7 +222,7 @@ func (d *decoder) decodeWithCode(code byte, rv reflect.Value) error { } if l > rv.Len() { - return fmt.Errorf("%v len is %d, but msgpack has %d elements, %w", rv.Type(), rv.Len(), l, ErrNotMatchArrayElement) + return fmt.Errorf("%v len is %d, but msgpack has %d elements, %w", rv.Type(), rv.Len(), l, def.ErrNotMatchArrayElement) } // create array dynamically @@ -320,10 +319,6 @@ func (d *decoder) decodeWithCode(code byte, rv reflect.Value) error { return nil } -var ErrNotMatchArrayElement = errors.New("not match array element") - -var ErrCanNotDecode = errors.New("msgpack : invalid code") - func (d *decoder) errorTemplate(code byte, k reflect.Kind) error { - return fmt.Errorf("msgpack : invalid code %x decoding %v, %w", code, k, ErrCanNotDecode) + return fmt.Errorf("msgpack : invalid code %x decoding %v, %w", code, k, def.ErrCanNotDecode) } diff --git a/internal/stream/decoding/interface.go b/internal/stream/decoding/interface.go index d0a0a11..d220214 100644 --- a/internal/stream/decoding/interface.go +++ b/internal/stream/decoding/interface.go @@ -1,7 +1,6 @@ package decoding import ( - "errors" "fmt" "reflect" @@ -167,15 +166,12 @@ func (d *decoder) asInterfaceWithCode(code byte, k reflect.Kind) (interface{}, e return nil, d.errorTemplate(code, k) } -var ErrCanNotSetSliceAsMapKey = errors.New("can not set slice as map key") -var ErrCanNotSetMapAsMapKey = errors.New("can not set map as map key") - func (d *decoder) canSetAsMapKey(code byte) error { switch { case d.isFixSlice(code), code == def.Array16, code == def.Array32: - return fmt.Errorf("%w. code: %x", ErrCanNotSetSliceAsMapKey, code) + return fmt.Errorf("%w. code: %x", def.ErrCanNotSetSliceAsMapKey, code) case d.isFixMap(code), code == def.Map16, code == def.Map32: - return fmt.Errorf("%w. code: %x", ErrCanNotSetMapAsMapKey, code) + return fmt.Errorf("%w. code: %x", def.ErrCanNotSetMapAsMapKey, code) } return nil } From 0097937d4cdfc0d23102423a7d99e59d5371c565 Mon Sep 17 00:00:00 2001 From: shamaton Date: Sat, 10 Aug 2024 11:55:27 +0900 Subject: [PATCH 23/41] fix stream decoding tests --- internal/stream/decoding/decoding_test.go | 8 ++++---- internal/stream/decoding/int_test.go | 2 +- internal/stream/decoding/interface_test.go | 4 ++-- internal/stream/decoding/slice_test.go | 2 +- internal/stream/decoding/string_test.go | 2 +- internal/stream/decoding/struct_test.go | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/internal/stream/decoding/decoding_test.go b/internal/stream/decoding/decoding_test.go index 0ab342a..741824a 100644 --- a/internal/stream/decoding/decoding_test.go +++ b/internal/stream/decoding/decoding_test.go @@ -254,7 +254,7 @@ func Test_decodeWithCode(t *testing.T) { Code: def.Int8, Data: []byte{}, ReadCount: 0, - Error: ErrCanNotDecode, + Error: def.ErrCanNotDecode, MethodAsWithCode: method, }, { @@ -538,7 +538,7 @@ func Test_decodeWithCode(t *testing.T) { Code: def.Bin8, Data: []byte{2, 1, 2}, ReadCount: 2, - Error: ErrNotMatchArrayElement, + Error: def.ErrNotMatchArrayElement, MethodAsWithCode: method, }, { @@ -570,7 +570,7 @@ func Test_decodeWithCode(t *testing.T) { Code: def.Str8, Data: []byte{2}, ReadCount: 1, - Error: ErrNotMatchArrayElement, + Error: def.ErrNotMatchArrayElement, MethodAsWithCode: method, }, { @@ -610,7 +610,7 @@ func Test_decodeWithCode(t *testing.T) { Code: def.Array16, Data: []byte{0, 2}, ReadCount: 1, - Error: ErrNotMatchArrayElement, + Error: def.ErrNotMatchArrayElement, MethodAsWithCode: method, }, { diff --git a/internal/stream/decoding/int_test.go b/internal/stream/decoding/int_test.go index 6224f52..65c6479 100644 --- a/internal/stream/decoding/int_test.go +++ b/internal/stream/decoding/int_test.go @@ -188,7 +188,7 @@ func Test_asIntWithCode(t *testing.T) { { Name: "Unexpected", Code: def.Array16, - Error: ErrCanNotDecode, + Error: def.ErrCanNotDecode, MethodAsWithCode: method, }, } diff --git a/internal/stream/decoding/interface_test.go b/internal/stream/decoding/interface_test.go index 42e7b1f..ed4d8bf 100644 --- a/internal/stream/decoding/interface_test.go +++ b/internal/stream/decoding/interface_test.go @@ -158,7 +158,7 @@ func Test_asInterfaceWithCode(t *testing.T) { Code: def.Map16, Data: []byte{0, 1, def.Array16}, ReadCount: 2, - Error: ErrCanNotSetSliceAsMapKey, + Error: def.ErrCanNotSetSliceAsMapKey, MethodAsWithCode: method, }, { @@ -166,7 +166,7 @@ func Test_asInterfaceWithCode(t *testing.T) { Code: def.Map16, Data: []byte{0, 1, def.Map16}, ReadCount: 2, - Error: ErrCanNotSetMapAsMapKey, + Error: def.ErrCanNotSetMapAsMapKey, MethodAsWithCode: method, }, { diff --git a/internal/stream/decoding/slice_test.go b/internal/stream/decoding/slice_test.go index 5de78f8..5a7b223 100644 --- a/internal/stream/decoding/slice_test.go +++ b/internal/stream/decoding/slice_test.go @@ -56,7 +56,7 @@ func Test_sliceLength(t *testing.T) { { Name: "Unexpected", Code: def.Nil, - Error: ErrCanNotDecode, + Error: def.ErrCanNotDecode, MethodAsWithCode: method, }, } diff --git a/internal/stream/decoding/string_test.go b/internal/stream/decoding/string_test.go index d44e1ca..9b32727 100644 --- a/internal/stream/decoding/string_test.go +++ b/internal/stream/decoding/string_test.go @@ -77,7 +77,7 @@ func Test_stringByteLength(t *testing.T) { { Name: "Unexpected", Code: def.Array16, - Error: ErrCanNotDecode, + Error: def.ErrCanNotDecode, MethodAsWithCode: method, }, } diff --git a/internal/stream/decoding/struct_test.go b/internal/stream/decoding/struct_test.go index cdc6278..48be31b 100644 --- a/internal/stream/decoding/struct_test.go +++ b/internal/stream/decoding/struct_test.go @@ -89,7 +89,7 @@ func Test_setStructFromMap(t *testing.T) { Code: def.Map16, Data: []byte{0, 1, def.FixStr + 1, 'v', def.Array16}, ReadCount: 4, - Error: ErrCanNotDecode, + Error: def.ErrCanNotDecode, MethodAsWithCode: method, }, { @@ -152,7 +152,7 @@ func Test_setStructFromArray(t *testing.T) { Code: def.Array16, Data: []byte{0, 1, def.Array16}, ReadCount: 2, - Error: ErrCanNotDecode, + Error: def.ErrCanNotDecode, MethodAsWithCode: method, }, { From 5ac2f33c741380e549c3f404dfa6ddda552cf424 Mon Sep 17 00:00:00 2001 From: shamaton Date: Sat, 10 Aug 2024 23:41:40 +0900 Subject: [PATCH 24/41] add test helper adn bin tests --- def/error.go | 2 + internal/common/testutil/testutil.go | 2 +- internal/decoding/bin.go | 21 +++++-- internal/decoding/bin_test.go | 93 ++++++++++++++++++++++++++++ internal/decoding/decoding.go | 3 +- internal/decoding/decoding_test.go | 64 +++++++++++++++++++ internal/decoding/read.go | 6 +- 7 files changed, 179 insertions(+), 12 deletions(-) create mode 100644 internal/decoding/bin_test.go create mode 100644 internal/decoding/decoding_test.go diff --git a/def/error.go b/def/error.go index 917512a..4b49182 100644 --- a/def/error.go +++ b/def/error.go @@ -7,4 +7,6 @@ var ( ErrCanNotDecode = errors.New("msgpack : invalid code") ErrCanNotSetSliceAsMapKey = errors.New("can not set slice as map key") ErrCanNotSetMapAsMapKey = errors.New("can not set map as map key") + + ErrTooShortBytes = errors.New("too short bytes") ) diff --git a/internal/common/testutil/testutil.go b/internal/common/testutil/testutil.go index d4054b1..fded725 100644 --- a/internal/common/testutil/testutil.go +++ b/internal/common/testutil/testutil.go @@ -10,7 +10,7 @@ import ( func NoError(t *testing.T, err error) { t.Helper() if err != nil { - t.Fatal(err) + t.Fatalf("error is not nil: %v", err) } } diff --git a/internal/decoding/bin.go b/internal/decoding/bin.go index 7789640..65fb859 100644 --- a/internal/decoding/bin.go +++ b/internal/decoding/bin.go @@ -28,22 +28,31 @@ func (d *decoder) asBin(offset int, k reflect.Kind) ([]byte, int, error) { if err != nil { return emptyBytes, 0, err } - o := offset + int(uint8(l)) - return d.data[offset:o], o, nil + v, offset, err := d.readSizeN(offset, int(uint8(l))) + if err != nil { + return emptyBytes, 0, err + } + return v, offset, nil case def.Bin16: bs, offset, err := d.readSize2(offset) - o := offset + int(binary.BigEndian.Uint16(bs)) if err != nil { return emptyBytes, 0, err } - return d.data[offset:o], o, nil + v, offset, err := d.readSizeN(offset, int(binary.BigEndian.Uint16(bs))) + if err != nil { + return emptyBytes, 0, err + } + return v, offset, nil case def.Bin32: bs, offset, err := d.readSize4(offset) - o := offset + int(binary.BigEndian.Uint32(bs)) if err != nil { return emptyBytes, 0, err } - return d.data[offset:o], o, nil + v, offset, err := d.readSizeN(offset, int(binary.BigEndian.Uint32(bs))) + if err != nil { + return emptyBytes, 0, err + } + return v, offset, nil } return emptyBytes, 0, d.errorTemplate(code, k) diff --git a/internal/decoding/bin_test.go b/internal/decoding/bin_test.go new file mode 100644 index 0000000..bd4d66c --- /dev/null +++ b/internal/decoding/bin_test.go @@ -0,0 +1,93 @@ +package decoding + +import ( + "reflect" + "testing" + + "github.com/shamaton/msgpack/v2/def" + tu "github.com/shamaton/msgpack/v2/internal/common/testutil" +) + +func Test_isCodeBin(t *testing.T) { + d := decoder{} + for i := 0x00; i <= 0xff; i++ { + v := byte(i) + isBin := v == def.Bin8 || v == def.Bin16 || v == def.Bin32 + tu.Equal(t, d.isCodeBin(v), isBin) + } +} + +func Test_asBin(t *testing.T) { + method := func(d *decoder) func(int, reflect.Kind) ([]byte, int, error) { + return d.asBin + } + testcases := AsXXXTestCases[[]byte]{ + { + Name: "error.code", + Data: []byte{}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Bin8.error.size", + Data: []byte{def.Bin8}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Bin8.error.data", + Data: []byte{def.Bin8, 1}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Bin8.ok", + Data: []byte{def.Bin8, 1, 'a'}, + Expected: []byte{'a'}, + MethodAs: method, + }, + { + Name: "Bin16.error.size", + Data: []byte{def.Bin16}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Bin16.error.data", + Data: []byte{def.Bin16, 0, 1}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Bin16.ok", + Data: []byte{def.Bin16, 0, 1, 'b'}, + Expected: []byte{'b'}, + MethodAs: method, + }, + { + Name: "Bin32.error.size", + Data: []byte{def.Bin32}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Bin32.error.data", + Data: []byte{def.Bin32, 0, 1}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Bin32.ok", + Data: []byte{def.Bin32, 0, 0, 0, 1, 'c'}, + Expected: []byte{'c'}, + MethodAs: method, + }, + { + Name: "Unexpected", + Data: []byte{def.Nil}, + Error: def.ErrCanNotDecode, + MethodAs: method, + }, + } + testcases.Run(t) +} diff --git a/internal/decoding/decoding.go b/internal/decoding/decoding.go index a1b064d..0c23892 100644 --- a/internal/decoding/decoding.go +++ b/internal/decoding/decoding.go @@ -4,6 +4,7 @@ import ( "fmt" "reflect" + "github.com/shamaton/msgpack/v2/def" "github.com/shamaton/msgpack/v2/internal/common" ) @@ -338,5 +339,5 @@ func (d *decoder) decode(rv reflect.Value, offset int) (int, error) { } func (d *decoder) errorTemplate(code byte, k reflect.Kind) error { - return fmt.Errorf("msgpack : invalid code %x decoding %v", code, k) + return fmt.Errorf("msgpack : invalid code %x decoding %v, %w", code, k, def.ErrCanNotDecode) } diff --git a/internal/decoding/decoding_test.go b/internal/decoding/decoding_test.go new file mode 100644 index 0000000..b17c32e --- /dev/null +++ b/internal/decoding/decoding_test.go @@ -0,0 +1,64 @@ +package decoding + +import ( + "reflect" + "testing" + + tu "github.com/shamaton/msgpack/v2/internal/common/testutil" +) + +type AsXXXTestCase[T any] struct { + Name string + Code byte + Data []byte + Expected T + Error error + MethodAs func(d *decoder) func(int, reflect.Kind) (T, int, error) + MethodAsWithCode func(d *decoder) func(byte, reflect.Kind) (T, int, error) + MethodAsCustom func(d *decoder) (T, int, error) +} + +type AsXXXTestCases[T any] []AsXXXTestCase[T] + +func (tcs AsXXXTestCases[T]) Run(t *testing.T) { + for _, tc := range tcs { + tc.Run(t) + } +} + +func (tc *AsXXXTestCase[T]) Run(t *testing.T) { + const kind = reflect.String + t.Helper() + + if tc.MethodAs == nil && tc.MethodAsWithCode == nil && tc.MethodAsCustom == nil { + t.Fatal("must set either method or methodAsWithCode or MethodAsCustom") + } + + methodAs := func(d *decoder) (T, int, error) { + if tc.MethodAs != nil { + return tc.MethodAs(d)(0, kind) + } + if tc.MethodAsWithCode != nil { + return tc.MethodAsWithCode(d)(tc.Code, kind) + } + if tc.MethodAsCustom != nil { + return tc.MethodAsCustom(d) + } + panic("unreachable") + } + + t.Run(tc.Name, func(t *testing.T) { + d := decoder{ + data: tc.Data, + } + + v, offset, err := methodAs(&d) + if tc.Error != nil { + tu.IsError(t, err, tc.Error) + return + } + tu.NoError(t, err) + tu.Equal(t, v, tc.Expected) + tu.Equal(t, offset, len(tc.Data)) + }) +} diff --git a/internal/decoding/read.go b/internal/decoding/read.go index 5d976bc..dcb9e32 100644 --- a/internal/decoding/read.go +++ b/internal/decoding/read.go @@ -1,15 +1,13 @@ package decoding import ( - "errors" - "github.com/shamaton/msgpack/v2/def" ) func (d *decoder) readSize1(index int) (byte, int, error) { rb := def.Byte1 if len(d.data) < index+rb { - return 0, 0, errors.New("too short bytes") + return 0, 0, def.ErrTooShortBytes } return d.data[index], index + rb, nil } @@ -28,7 +26,7 @@ func (d *decoder) readSize8(index int) ([]byte, int, error) { func (d *decoder) readSizeN(index, n int) ([]byte, int, error) { if len(d.data) < index+n { - return emptyBytes, 0, errors.New("too short bytes") + return emptyBytes, 0, def.ErrTooShortBytes } return d.data[index : index+n], index + n, nil } From 7155d3719575bec43fad0c1cf31242c3b85b4c87 Mon Sep 17 00:00:00 2001 From: shamaton Date: Thu, 15 Aug 2024 19:12:21 +0900 Subject: [PATCH 25/41] fix bugs and add decoding tests --- def/error.go | 4 +- internal/decoding/bin_test.go | 2 +- internal/decoding/bool.go | 6 +- internal/decoding/bool_test.go | 40 ++ internal/decoding/complex_test.go | 148 ++++++ internal/decoding/decoding.go | 6 +- internal/decoding/decoding_test.go | 703 +++++++++++++++++++++++++++- internal/decoding/ext_test.go | 22 + internal/decoding/float.go | 8 +- internal/decoding/float_test.go | 314 +++++++++++++ internal/decoding/int_test.go | 167 +++++++ internal/decoding/interface.go | 6 +- internal/decoding/interface_test.go | 191 ++++++++ internal/decoding/map.go | 3 +- internal/decoding/map_test.go | 561 ++++++++++++++++++++++ internal/decoding/slice.go | 3 +- internal/decoding/slice_test.go | 469 +++++++++++++++++++ internal/decoding/string_test.go | 121 +++++ internal/decoding/struct_test.go | 466 ++++++++++++++++++ internal/decoding/uint_test.go | 144 ++++++ 20 files changed, 3352 insertions(+), 32 deletions(-) create mode 100644 internal/decoding/bool_test.go create mode 100644 internal/decoding/complex_test.go create mode 100644 internal/decoding/ext_test.go create mode 100644 internal/decoding/float_test.go create mode 100644 internal/decoding/int_test.go create mode 100644 internal/decoding/interface_test.go create mode 100644 internal/decoding/map_test.go create mode 100644 internal/decoding/slice_test.go create mode 100644 internal/decoding/string_test.go create mode 100644 internal/decoding/struct_test.go create mode 100644 internal/decoding/uint_test.go diff --git a/def/error.go b/def/error.go index 4b49182..f5b2fea 100644 --- a/def/error.go +++ b/def/error.go @@ -8,5 +8,7 @@ var ( ErrCanNotSetSliceAsMapKey = errors.New("can not set slice as map key") ErrCanNotSetMapAsMapKey = errors.New("can not set map as map key") - ErrTooShortBytes = errors.New("too short bytes") + ErrTooShortBytes = errors.New("too short bytes") + ErrLackDataLengthToSlice = errors.New("data length lacks to create slice") + ErrLackDataLengthToMap = errors.New("data length lacks to create map") ) diff --git a/internal/decoding/bin_test.go b/internal/decoding/bin_test.go index bd4d66c..d20df30 100644 --- a/internal/decoding/bin_test.go +++ b/internal/decoding/bin_test.go @@ -72,7 +72,7 @@ func Test_asBin(t *testing.T) { }, { Name: "Bin32.error.data", - Data: []byte{def.Bin32, 0, 1}, + Data: []byte{def.Bin32, 0, 0, 0, 1}, Error: def.ErrTooShortBytes, MethodAs: method, }, diff --git a/internal/decoding/bool.go b/internal/decoding/bool.go index 7771146..7d8809c 100644 --- a/internal/decoding/bool.go +++ b/internal/decoding/bool.go @@ -7,8 +7,10 @@ import ( ) func (d *decoder) asBool(offset int, k reflect.Kind) (bool, int, error) { - code := d.data[offset] - offset++ + code, offset, err := d.readSize1(offset) + if err != nil { + return false, 0, err + } switch code { case def.True: diff --git a/internal/decoding/bool_test.go b/internal/decoding/bool_test.go new file mode 100644 index 0000000..e232dd1 --- /dev/null +++ b/internal/decoding/bool_test.go @@ -0,0 +1,40 @@ +package decoding + +import ( + "github.com/shamaton/msgpack/v2/def" + "reflect" + "testing" +) + +func Test_asBool(t *testing.T) { + method := func(d *decoder) func(int, reflect.Kind) (bool, int, error) { + return d.asBool + } + testcases := AsXXXTestCases[bool]{ + { + Name: "error.code", + Data: []byte{}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Bool.false", + Data: []byte{def.False}, + Expected: false, + MethodAs: method, + }, + { + Name: "Bool.true", + Data: []byte{def.True}, + Expected: true, + MethodAs: method, + }, + { + Name: "Unexpected", + Data: []byte{def.Nil}, + Error: def.ErrCanNotDecode, + MethodAs: method, + }, + } + testcases.Run(t) +} diff --git a/internal/decoding/complex_test.go b/internal/decoding/complex_test.go new file mode 100644 index 0000000..61bcdd7 --- /dev/null +++ b/internal/decoding/complex_test.go @@ -0,0 +1,148 @@ +package decoding + +import ( + "reflect" + "testing" + + "github.com/shamaton/msgpack/v2/def" +) + +func Test_asComplex64(t *testing.T) { + method := func(d *decoder) func(int, reflect.Kind) (complex64, int, error) { + return d.asComplex64 + } + testcases := AsXXXTestCases[complex64]{ + { + Name: "error.code", + Data: []byte{}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Fixext8.error.type", + Data: []byte{def.Fixext8}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Fixext8.error.r", + Data: []byte{def.Fixext8, byte(def.ComplexTypeCode())}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Fixext8.error.i", + Data: []byte{def.Fixext8, byte(def.ComplexTypeCode()), 0, 0, 0, 1}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Fixext8.ok", + Data: []byte{def.Fixext8, byte(def.ComplexTypeCode()), 63, 128, 0, 0, 63, 128, 0, 0}, + Expected: complex(1, 1), + MethodAs: method, + }, + { + Name: "Fixext16.error.type", + Data: []byte{def.Fixext16}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Fixext16.error.r", + Data: []byte{def.Fixext16, byte(def.ComplexTypeCode())}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Fixext16.error.i", + Data: []byte{def.Fixext16, byte(def.ComplexTypeCode()), 0, 0, 0, 1}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Fixext16.ok", + Data: []byte{def.Fixext16, byte(def.ComplexTypeCode()), + 63, 240, 0, 0, 0, 0, 0, 0, 63, 240, 0, 0, 0, 0, 0, 0}, + Expected: complex(1, 1), + MethodAs: method, + }, + { + Name: "Unexpected", + Data: []byte{def.Nil}, + Error: def.ErrCanNotDecode, + MethodAs: method, + }, + } + testcases.Run(t) +} + +func Test_asComplex128(t *testing.T) { + method := func(d *decoder) func(int, reflect.Kind) (complex128, int, error) { + return d.asComplex128 + } + testcases := AsXXXTestCases[complex128]{ + { + Name: "error.code", + Data: []byte{}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Fixext8.error.type", + Data: []byte{def.Fixext8}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Fixext8.error.r", + Data: []byte{def.Fixext8, byte(def.ComplexTypeCode())}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Fixext8.error.i", + Data: []byte{def.Fixext8, byte(def.ComplexTypeCode()), 0, 0, 0, 1}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Fixext8.ok", + Data: []byte{def.Fixext8, byte(def.ComplexTypeCode()), 63, 128, 0, 0, 63, 128, 0, 0}, + Expected: complex(1, 1), + MethodAs: method, + }, + { + Name: "Fixext16.error.type", + Data: []byte{def.Fixext16}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Fixext16.error.r", + Data: []byte{def.Fixext16, byte(def.ComplexTypeCode())}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Fixext16.error.i", + Data: []byte{def.Fixext16, byte(def.ComplexTypeCode()), 0, 0, 0, 1}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Fixext16.ok", + Data: []byte{def.Fixext16, byte(def.ComplexTypeCode()), + 63, 240, 0, 0, 0, 0, 0, 0, 63, 240, 0, 0, 0, 0, 0, 0}, + Expected: complex(1, 1), + MethodAs: method, + }, + { + Name: "Unexpected", + Data: []byte{def.Nil}, + Error: def.ErrCanNotDecode, + MethodAs: method, + }, + } + testcases.Run(t) +} diff --git a/internal/decoding/decoding.go b/internal/decoding/decoding.go index 0c23892..393f0c3 100644 --- a/internal/decoding/decoding.go +++ b/internal/decoding/decoding.go @@ -192,7 +192,7 @@ func (d *decoder) decode(rv reflect.Value, offset int) (int, error) { return 0, err } if len(bs) > rv.Len() { - return 0, fmt.Errorf("%v len is %d, but msgpack has %d elements", rv.Type(), rv.Len(), len(bs)) + return 0, fmt.Errorf("%v len is %d, but msgpack has %d elements, %w", rv.Type(), rv.Len(), len(bs), def.ErrNotMatchArrayElement) } for i, b := range bs { rv.Index(i).SetUint(uint64(b)) @@ -206,7 +206,7 @@ func (d *decoder) decode(rv reflect.Value, offset int) (int, error) { return 0, err } if l > rv.Len() { - return 0, fmt.Errorf("%v len is %d, but msgpack has %d elements", rv.Type(), rv.Len(), l) + return 0, fmt.Errorf("%v len is %d, but msgpack has %d elements, %w", rv.Type(), rv.Len(), l, def.ErrNotMatchArrayElement) } bs, offset, err := d.asStringByteByLength(offset, l, k) if err != nil { @@ -225,7 +225,7 @@ func (d *decoder) decode(rv reflect.Value, offset int) (int, error) { } if l > rv.Len() { - return 0, fmt.Errorf("%v len is %d, but msgpack has %d elements", rv.Type(), rv.Len(), l) + return 0, fmt.Errorf("%v len is %d, but msgpack has %d elements, %w", rv.Type(), rv.Len(), l, def.ErrNotMatchArrayElement) } if err = d.hasRequiredLeastSliceSize(o, l); err != nil { diff --git a/internal/decoding/decoding_test.go b/internal/decoding/decoding_test.go index b17c32e..ec7a564 100644 --- a/internal/decoding/decoding_test.go +++ b/internal/decoding/decoding_test.go @@ -4,18 +4,17 @@ import ( "reflect" "testing" + "github.com/shamaton/msgpack/v2/def" tu "github.com/shamaton/msgpack/v2/internal/common/testutil" ) type AsXXXTestCase[T any] struct { - Name string - Code byte - Data []byte - Expected T - Error error - MethodAs func(d *decoder) func(int, reflect.Kind) (T, int, error) - MethodAsWithCode func(d *decoder) func(byte, reflect.Kind) (T, int, error) - MethodAsCustom func(d *decoder) (T, int, error) + Name string + Data []byte + Expected T + Error error + MethodAs func(d *decoder) func(int, reflect.Kind) (T, int, error) + MethodAsCustom func(d *decoder) (int, T, error) } type AsXXXTestCases[T any] []AsXXXTestCase[T] @@ -30,19 +29,17 @@ func (tc *AsXXXTestCase[T]) Run(t *testing.T) { const kind = reflect.String t.Helper() - if tc.MethodAs == nil && tc.MethodAsWithCode == nil && tc.MethodAsCustom == nil { - t.Fatal("must set either method or methodAsWithCode or MethodAsCustom") + if tc.MethodAs == nil && tc.MethodAsCustom == nil { + t.Fatal("must set either method or MethodAsCustom") } methodAs := func(d *decoder) (T, int, error) { if tc.MethodAs != nil { return tc.MethodAs(d)(0, kind) } - if tc.MethodAsWithCode != nil { - return tc.MethodAsWithCode(d)(tc.Code, kind) - } if tc.MethodAsCustom != nil { - return tc.MethodAsCustom(d) + v, o, err := tc.MethodAsCustom(d) + return o, v, err } panic("unreachable") } @@ -62,3 +59,681 @@ func (tc *AsXXXTestCase[T]) Run(t *testing.T) { tu.Equal(t, offset, len(tc.Data)) }) } + +func TestDecoding(t *testing.T) { + t.Run("empty data", func(t *testing.T) { + v := new(int) + err := Decode(nil, v, false) + tu.Error(t, err) + tu.Equal(t, err.Error(), "data is empty") + }) +} + +func Test_decodeWithCode(t *testing.T) { + var target any + method := func(d *decoder) func(offset int, _ reflect.Kind) (bool, int, error) { + return func(offset int, _ reflect.Kind) (bool, int, error) { + rv := reflect.ValueOf(target) + o, err := d.decode(rv.Elem(), offset) + return true, o, err + } + } + + t.Run("Int", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{def.Int8}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "ok", + Data: []byte{def.Int8, 5}, + Expected: true, + MethodAs: method, + }, + } + v := new(int) + target = v + testcases.Run(t) + tu.Equal(t, *v, 5) + }) + t.Run("Uint", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{def.Uint8}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "ok", + Data: []byte{def.Uint8, 5}, + Expected: true, + MethodAs: method, + }, + } + v := new(uint) + target = v + testcases.Run(t) + tu.Equal(t, *v, 5) + }) + t.Run("Float32", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{def.Float32}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "ok", + Data: []byte{def.Float32, 63, 128, 0, 0}, + Expected: true, + MethodAs: method, + }, + } + v := new(float32) + target = v + testcases.Run(t) + tu.Equal(t, *v, 1) + }) + t.Run("Float64", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{def.Float64}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "ok", + Data: []byte{def.Float64, 63, 240, 0, 0, 0, 0, 0, 0}, + Expected: true, + MethodAs: method, + }, + } + v := new(float64) + target = v + testcases.Run(t) + tu.Equal(t, *v, 1) + }) + t.Run("BinString", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{def.Bin8}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "ok", + Data: []byte{def.Bin8, 1, 'a'}, + Expected: true, + MethodAs: method, + }, + } + v := new(string) + target = v + testcases.Run(t) + tu.Equal(t, *v, "a") + }) + t.Run("String", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{def.Str8}, + Expected: false, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "ok", + Data: []byte{def.Str8, 1, 'b'}, + Expected: true, + MethodAs: method, + }, + } + v := new(string) + target = v + testcases.Run(t) + tu.Equal(t, *v, "b") + }) + t.Run("Bool", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{def.Int8}, + Error: def.ErrCanNotDecode, + MethodAs: method, + }, + { + Name: "ok", + Data: []byte{def.True}, + Expected: true, + MethodAs: method, + }, + } + v := new(bool) + target = v + testcases.Run(t) + tu.Equal(t, *v, true) + }) + t.Run("Slice.nil", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "ok", + Data: []byte{def.Nil}, + Expected: true, + MethodAs: method, + }, + } + v := new([]int) + target = v + testcases.Run(t) + tu.Equal(t, *v, nil) + }) + t.Run("Slice.bin", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{def.Bin8}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "ok", + Data: []byte{def.Bin8, 1, 2}, + Expected: true, + MethodAs: method, + }, + } + v := new([]byte) + target = v + testcases.Run(t) + tu.Equal(t, *v, []byte{2}) + }) + t.Run("Slice.string", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error.strlen", + Data: []byte{def.Str8}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "error.bytelen", + Data: []byte{def.Str8, 1}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "ok", + Data: []byte{def.Str8, 1, 'c'}, + Expected: true, + MethodAs: method, + }, + } + v := new([]byte) + target = v + testcases.Run(t) + tu.Equal(t, *v, []byte{'c'}) + }) + t.Run("Slice.fixed", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error.strlen", + Data: []byte{def.Array16}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "error.require", + Data: []byte{def.Array16, 0, 1}, + Error: def.ErrLackDataLengthToSlice, + MethodAs: method, + }, + { + Name: "error.slice", + Data: []byte{def.Array16, 0, 1, def.Int8}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "ok", + Data: []byte{def.Array16, 0, 1, def.PositiveFixIntMin + 3}, + Expected: true, + MethodAs: method, + }, + } + v := new([]int) + target = v + testcases.Run(t) + tu.Equal(t, *v, []int{3}) + }) + t.Run("Slice.struct", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error.strlen", + Data: []byte{def.Array16}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "error.require", + Data: []byte{def.Array16, 0, 1}, + Error: def.ErrLackDataLengthToSlice, + MethodAs: method, + }, + { + Name: "error.slice", + Data: []byte{def.Array16, 0, 1, def.Map16}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "error.struct", + Data: []byte{def.Array16, 0, 1, def.FixMap + 1, def.FixStr + 1, 'v'}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "ok", + Data: []byte{def.Array16, 0, 1, def.FixMap + 1, def.FixStr + 1, 'v', def.PositiveFixIntMin + 3}, + Expected: true, + MethodAs: method, + }, + } + type st struct { + V int `msgpack:"v"` + } + v := new([]st) + target = v + testcases.Run(t) + tu.Equal(t, *v, []st{{V: 3}}) + }) + t.Run("Slice.map", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error.strlen", + Data: []byte{def.Array16}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "error.require", + Data: []byte{def.Array16, 0, 1}, + Error: def.ErrLackDataLengthToSlice, + MethodAs: method, + }, + { + Name: "error.slice", + Data: []byte{def.Array16, 0, 1, def.Map16}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "error.map", + Data: []byte{def.Array16, 0, 1, def.FixMap + 1, def.FixStr + 1, 'v'}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "ok", + Data: []byte{def.Array16, 0, 1, def.FixMap + 1, def.FixStr + 1, 'v', def.PositiveFixIntMin + 3}, + Expected: true, + MethodAs: method, + }, + } + v := new([]map[string]int) + target = v + testcases.Run(t) + tu.Equal(t, *v, []map[string]int{{"v": 3}}) + }) + t.Run("Complex64", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{def.Fixext8}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "ok", + Data: []byte{def.Fixext8, byte(def.ComplexTypeCode()), 63, 128, 0, 0, 63, 128, 0, 0}, + Expected: true, + MethodAs: method, + }, + } + v := new(complex64) + target = v + testcases.Run(t) + tu.Equal(t, *v, complex(1, 1)) + }) + t.Run("Complex128", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{def.Fixext8}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "ok", + Data: []byte{def.Fixext8, byte(def.ComplexTypeCode()), 63, 128, 0, 0, 63, 128, 0, 0}, + Expected: true, + MethodAs: method, + }, + } + v := new(complex128) + target = v + testcases.Run(t) + tu.Equal(t, *v, complex(1, 1)) + }) + + t.Run("Array.nil", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "ok", + Data: []byte{def.Nil}, + Expected: true, + MethodAs: method, + }, + } + v := new([1]int) + target = v + testcases.Run(t) + tu.Equal(t, *v, [1]int{}) + }) + t.Run("Array.bin", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error.bin", + Data: []byte{def.Bin8}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "error.len", + Data: []byte{def.Bin8, 2, 1, 2}, + Error: def.ErrNotMatchArrayElement, + MethodAs: method, + }, + { + Name: "ok", + Data: []byte{def.Bin8, 1, 2}, + Expected: true, + MethodAs: method, + }, + } + v := new([1]byte) + target = v + testcases.Run(t) + tu.Equal(t, *v, [1]byte{2}) + }) + t.Run("Array.string", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error.strlen", + Data: []byte{def.Str8}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "error.compare", + Data: []byte{def.Str8, 2}, + Error: def.ErrNotMatchArrayElement, + MethodAs: method, + }, + { + Name: "error.bytelen", + Data: []byte{def.Str8, 1}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "ok", + Data: []byte{def.Str8, 1, 'c'}, + Expected: true, + MethodAs: method, + }, + } + v := new([1]byte) + target = v + testcases.Run(t) + tu.Equal(t, *v, [1]byte{'c'}) + }) + t.Run("Array.struct", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error.strlen", + Data: []byte{def.Array16}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "error.len.match", + Data: []byte{def.Array16, 0, 2}, + Error: def.ErrNotMatchArrayElement, + MethodAs: method, + }, + { + Name: "error.slice", + Data: []byte{def.Array16, 0, 1}, + Error: def.ErrLackDataLengthToSlice, + MethodAs: method, + }, + { + Name: "error.struct", + Data: []byte{def.Array16, 0, 1, def.FixMap + 1, def.FixStr + 1, 'v'}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "ok", + Data: []byte{def.Array16, 0, 1, def.FixMap + 1, def.FixStr + 1, 'v', def.PositiveFixIntMin + 3}, + Expected: true, + MethodAs: method, + }, + } + type st struct { + V int `msgpack:"v"` + } + v := new([1]st) + target = v + testcases.Run(t) + tu.Equal(t, *v, [1]st{{V: 3}}) + }) + t.Run("Map.nil", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "ok", + Data: []byte{def.Nil}, + Expected: true, + MethodAs: method, + }, + } + v := new(map[string]int) + target = v + testcases.Run(t) + tu.Equal(t, *v, nil) + }) + t.Run("Map.fixed", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error.strlen", + Data: []byte{def.Map16}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "error.require", + Data: []byte{def.Map16, 0, 1}, + Error: def.ErrLackDataLengthToMap, + MethodAs: method, + }, + { + Name: "error.map", + Data: []byte{def.Map16, 0, 1, def.Str16, 0}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "ok", + Data: []byte{def.Map16, 0, 1, def.FixStr + 1, 'a', def.PositiveFixIntMin + 3}, + Expected: true, + MethodAs: method, + }, + } + v := new(map[string]int) + target = v + testcases.Run(t) + tu.Equal(t, *v, map[string]int{"a": 3}) + }) + t.Run("Map.struct", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error.strlen", + Data: []byte{def.Map16}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "error.require", + Data: []byte{def.Map16, 0, 1}, + Error: def.ErrLackDataLengthToMap, + MethodAs: method, + }, + { + Name: "error.key", + Data: []byte{def.Map16, 0, 1, def.Str16, 0}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "error.value", + Data: []byte{def.Map16, 0, 1, def.FixStr + 1, 'a', def.FixMap + 1, def.FixStr + 1, 'v'}, + Expected: true, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "ok", + Data: []byte{def.Map16, 0, 1, def.FixStr + 1, 'a', def.FixMap + 1, def.FixStr + 1, 'v', def.PositiveFixIntMin + 3}, + Expected: true, + MethodAs: method, + }, + } + type st struct { + V int `msgpack:"v"` + } + v := new(map[string]st) + target = v + testcases.Run(t) + tu.Equal(t, *v, map[string]st{"a": {V: 3}}) + }) + t.Run("Struct", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{def.Map16}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "ok", + Data: []byte{def.Map16, 0, 1, def.FixStr + 1, 'v', def.PositiveFixIntMin + 3}, + Expected: true, + MethodAs: method, + }, + } + type st struct { + V int `msgpack:"v"` + } + v := new(st) + target = v + testcases.Run(t) + tu.Equal(t, *v, st{V: 3}) + }) + t.Run("Ptr.nil", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "ok", + Data: []byte{def.Nil}, + Expected: true, + MethodAs: method, + }, + } + v := new(int) + target = &v + testcases.Run(t) + tu.Equal(t, *v, 0) + }) + t.Run("Ptr", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{def.Int8}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "ok", + Data: []byte{def.Int8, 3}, + Expected: true, + MethodAs: method, + }, + } + v := new(int) + target = &v + testcases.Run(t) + tu.Equal(t, *v, 3) + }) + t.Run("Interface.ptr", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{def.Int8}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "ok", + Data: []byte{def.Int8, 3}, + Expected: true, + MethodAs: method, + }, + } + var v interface{} + v = new(int) + target = &v + testcases.Run(t) + vv := v.(*int) + tu.Equal(t, *vv, 3) + }) + t.Run("Interface", func(t *testing.T) { + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{def.Map16, 0, 1, def.FixStr + 1, 'v', def.Int8}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "ok", + Data: []byte{def.Map16, 0, 1, def.FixStr + 1, 'v', def.Int8, 3}, + Expected: true, + MethodAs: method, + }, + } + type st struct { + V any `msgpack:"v"` + } + v := new(st) + target = v + testcases.Run(t) + var vv any = int8(3) + tu.Equal(t, v.V, vv) + }) +} diff --git a/internal/decoding/ext_test.go b/internal/decoding/ext_test.go new file mode 100644 index 0000000..51160b3 --- /dev/null +++ b/internal/decoding/ext_test.go @@ -0,0 +1,22 @@ +package decoding + +import ( + "testing" + + tu "github.com/shamaton/msgpack/v2/internal/common/testutil" + "github.com/shamaton/msgpack/v2/time" +) + +func Test_AddExtDecoder(t *testing.T) { + t.Run("ignore", func(t *testing.T) { + AddExtDecoder(time.Decoder) + tu.Equal(t, len(extCoders), 1) + }) +} + +func Test_RemoveExtDecoder(t *testing.T) { + t.Run("ignore", func(t *testing.T) { + RemoveExtDecoder(time.Decoder) + tu.Equal(t, len(extCoders), 1) + }) +} diff --git a/internal/decoding/float.go b/internal/decoding/float.go index 8483ba5..62f9e0b 100644 --- a/internal/decoding/float.go +++ b/internal/decoding/float.go @@ -27,14 +27,14 @@ func (d *decoder) asFloat32(offset int, k reflect.Kind) (float32, int, error) { case d.isPositiveFixNum(code), code == def.Uint8, code == def.Uint16, code == def.Uint32, code == def.Uint64: v, offset, err := d.asUint(offset, k) if err != nil { - break + return 0, 0, err } return float32(v), offset, nil case d.isNegativeFixNum(code), code == def.Int8, code == def.Int16, code == def.Int32, code == def.Int64: v, offset, err := d.asInt(offset, k) if err != nil { - break + return 0, 0, err } return float32(v), offset, nil @@ -73,14 +73,14 @@ func (d *decoder) asFloat64(offset int, k reflect.Kind) (float64, int, error) { case d.isPositiveFixNum(code), code == def.Uint8, code == def.Uint16, code == def.Uint32, code == def.Uint64: v, offset, err := d.asUint(offset, k) if err != nil { - break + return 0, 0, err } return float64(v), offset, nil case d.isNegativeFixNum(code), code == def.Int8, code == def.Int16, code == def.Int32, code == def.Int64: v, offset, err := d.asInt(offset, k) if err != nil { - break + return 0, 0, err } return float64(v), offset, nil diff --git a/internal/decoding/float_test.go b/internal/decoding/float_test.go new file mode 100644 index 0000000..5fa75b5 --- /dev/null +++ b/internal/decoding/float_test.go @@ -0,0 +1,314 @@ +package decoding + +import ( + "reflect" + "testing" + + "github.com/shamaton/msgpack/v2/def" +) + +func Test_asFloat32(t *testing.T) { + method := func(d *decoder) func(int, reflect.Kind) (float32, int, error) { + return d.asFloat32 + } + testcases := AsXXXTestCases[float32]{ + { + Name: "error.code", + Data: []byte{}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Float32.error", + Data: []byte{def.Float32}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Float32.ok", + Data: []byte{def.Float32, 63, 128, 0, 0}, + Expected: float32(1), + MethodAs: method, + }, + { + Name: "PositiveFixNum.ok", + Data: []byte{def.PositiveFixIntMin + 1}, + Expected: float32(1), + MethodAs: method, + }, + { + Name: "Uint8.error", + Data: []byte{def.Uint8}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Uint8.ok", + Data: []byte{def.Uint8, 1}, + Expected: float32(1), + MethodAs: method, + }, + { + Name: "Uint16.error", + Data: []byte{def.Uint16}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Uint16.ok", + Data: []byte{def.Uint16, 0, 1}, + Expected: float32(1), + MethodAs: method, + }, + { + Name: "Uint32.error", + Data: []byte{def.Uint32}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Uint32.ok", + Data: []byte{def.Uint32, 0, 0, 0, 1}, + Expected: float32(1), + MethodAs: method, + }, + { + Name: "Uint64.error", + Data: []byte{def.Uint64}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Uint64.ok", + Data: []byte{def.Uint64, 0, 0, 0, 0, 0, 0, 0, 1}, + Expected: float32(1), + MethodAs: method, + }, + { + Name: "NegativeFixNum.ok", + Data: []byte{0xff}, + Expected: float32(-1), + MethodAs: method, + }, + { + Name: "Int8.error", + Data: []byte{def.Int8}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Int8.ok", + Data: []byte{def.Int8, 0xff}, + Expected: float32(-1), + MethodAs: method, + }, + { + Name: "Int16.error", + Data: []byte{def.Int16}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Int16.ok", + Data: []byte{def.Int16, 0xff, 0xff}, + Expected: float32(-1), + MethodAs: method, + }, + { + Name: "Int32.error", + Data: []byte{def.Int32}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Int32.ok", + Data: []byte{def.Int32, 0xff, 0xff, 0xff, 0xff}, + Expected: float32(-1), + MethodAs: method, + }, + { + Name: "Int64.error", + Data: []byte{def.Int64}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Int64.ok", + Data: []byte{def.Int64, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + Expected: float32(-1), + MethodAs: method, + }, + { + Name: "Nil.ok", + Data: []byte{def.Nil}, + Expected: float32(0), + MethodAs: method, + }, + { + Name: "Unexpected", + Data: []byte{def.Str8}, + Error: def.ErrCanNotDecode, + MethodAs: method, + }, + } + testcases.Run(t) +} + +func Test_asFloat64(t *testing.T) { + method := func(d *decoder) func(int, reflect.Kind) (float64, int, error) { + return d.asFloat64 + } + testcases := AsXXXTestCases[float64]{ + { + Name: "error.code", + Data: []byte{}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Float64.error", + Data: []byte{def.Float64}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Float64.ok", + Data: []byte{def.Float64, 63, 240, 0, 0, 0, 0, 0, 0}, + Expected: float64(1), + MethodAs: method, + }, + { + Name: "Float32.error", + Data: []byte{def.Float32}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Float32.ok", + Data: []byte{def.Float32, 63, 128, 0, 0}, + Expected: float64(1), + MethodAs: method, + }, + { + Name: "PositiveFixNum.ok", + Data: []byte{def.PositiveFixIntMin + 1}, + Expected: float64(1), + MethodAs: method, + }, + { + Name: "Uint8.error", + Data: []byte{def.Uint8}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Uint8.ok", + Data: []byte{def.Uint8, 1}, + Expected: float64(1), + MethodAs: method, + }, + { + Name: "Uint16.error", + Data: []byte{def.Uint16}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Uint16.ok", + Data: []byte{def.Uint16, 0, 1}, + Expected: float64(1), + MethodAs: method, + }, + { + Name: "Uint32.error", + Data: []byte{def.Uint32}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Uint32.ok", + Data: []byte{def.Uint32, 0, 0, 0, 1}, + Expected: float64(1), + MethodAs: method, + }, + { + Name: "Uint64.error", + Data: []byte{def.Uint64}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Uint64.ok", + Data: []byte{def.Uint64, 0, 0, 0, 0, 0, 0, 0, 1}, + Expected: float64(1), + MethodAs: method, + }, + { + Name: "NegativeFixNum.ok", + Data: []byte{0xff}, + Expected: float64(-1), + MethodAs: method, + }, + { + Name: "Int8.error", + Data: []byte{def.Int8}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Int8.ok", + Data: []byte{def.Int8, 0xff}, + Expected: float64(-1), + MethodAs: method, + }, + { + Name: "Int16.error", + Data: []byte{def.Int16}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Int16.ok", + Data: []byte{def.Int16, 0xff, 0xff}, + Expected: float64(-1), + MethodAs: method, + }, + { + Name: "Int32.error", + Data: []byte{def.Int32}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Int32.ok", + Data: []byte{def.Int32, 0xff, 0xff, 0xff, 0xff}, + Expected: float64(-1), + MethodAs: method, + }, + { + Name: "Int64.error", + Data: []byte{def.Int64}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Int64.ok", + Data: []byte{def.Int64, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + Expected: float64(-1), + MethodAs: method, + }, + { + Name: "Nil.ok", + Data: []byte{def.Nil}, + Expected: float64(0), + MethodAs: method, + }, + { + Name: "Unexpected", + Data: []byte{def.Str8}, + Error: def.ErrCanNotDecode, + MethodAs: method, + }, + } + testcases.Run(t) +} diff --git a/internal/decoding/int_test.go b/internal/decoding/int_test.go new file mode 100644 index 0000000..03d1b68 --- /dev/null +++ b/internal/decoding/int_test.go @@ -0,0 +1,167 @@ +package decoding + +import ( + "reflect" + "testing" + + "github.com/shamaton/msgpack/v2/def" +) + +func Test_asInt(t *testing.T) { + method := func(d *decoder) func(int, reflect.Kind) (int64, int, error) { + return d.asInt + } + testcases := AsXXXTestCases[int64]{ + { + Name: "error.code", + Data: []byte{}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "PositiveFixNum.ok", + Data: []byte{def.PositiveFixIntMin + 1}, + Expected: int64(1), + MethodAs: method, + }, + { + Name: "Uint8.error", + Data: []byte{def.Uint8}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Uint8.ok", + Data: []byte{def.Uint8, 1}, + Expected: int64(1), + MethodAs: method, + }, + { + Name: "Uint16.error", + Data: []byte{def.Uint16}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Uint16.ok", + Data: []byte{def.Uint16, 0, 1}, + Expected: int64(1), + MethodAs: method, + }, + { + Name: "Uint32.error", + Data: []byte{def.Uint32}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Uint32.ok", + Data: []byte{def.Uint32, 0, 0, 0, 1}, + Expected: int64(1), + MethodAs: method, + }, + { + Name: "Uint64.error", + Data: []byte{def.Uint64}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Uint64.ok", + Data: []byte{def.Uint64, 0, 0, 0, 0, 0, 0, 0, 1}, + Expected: int64(1), + MethodAs: method, + }, + { + Name: "NegativeFixNum.ok", + Data: []byte{0xff}, + Expected: int64(-1), + MethodAs: method, + }, + { + Name: "Int8.error", + Data: []byte{def.Int8}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Int8.ok", + Data: []byte{def.Int8, 0xff}, + Expected: int64(-1), + MethodAs: method, + }, + { + Name: "Int16.error", + Data: []byte{def.Int16}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Int16.ok", + Data: []byte{def.Int16, 0xff, 0xff}, + Expected: int64(-1), + MethodAs: method, + }, + { + Name: "Int32.error", + Data: []byte{def.Int32}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Int32.ok", + Data: []byte{def.Int32, 0xff, 0xff, 0xff, 0xff}, + Expected: int64(-1), + MethodAs: method, + }, + { + Name: "Int64.error", + Data: []byte{def.Int64}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Int64.ok", + Data: []byte{def.Int64, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + Expected: int64(-1), + MethodAs: method, + }, + { + Name: "Float32.error", + Data: []byte{def.Float32}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Float32.ok", + Data: []byte{def.Float32, 63, 128, 0, 0}, + Expected: int64(1), + MethodAs: method, + }, + { + Name: "Float64.error", + Data: []byte{def.Float64}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Float64.ok", + Data: []byte{def.Float64, 63, 240, 0, 0, 0, 0, 0, 0}, + Expected: int64(1), + MethodAs: method, + }, + { + Name: "Nil.ok", + Data: []byte{def.Nil}, + Expected: int64(0), + MethodAs: method, + }, + { + Name: "Unexpected", + Data: []byte{def.Str8}, + Error: def.ErrCanNotDecode, + MethodAs: method, + }, + } + testcases.Run(t) +} diff --git a/internal/decoding/interface.go b/internal/decoding/interface.go index 537ac3f..c47a581 100644 --- a/internal/decoding/interface.go +++ b/internal/decoding/interface.go @@ -134,7 +134,7 @@ func (d *decoder) asInterface(offset int, k reflect.Kind) (interface{}, int, err } v := make(map[interface{}]interface{}, l) for i := 0; i < l; i++ { - if d.canSetAsMapKey(o) != nil { + if err := d.canSetAsMapKey(o); err != nil { return nil, 0, err } key, o2, err := d.asInterface(o, k) @@ -182,9 +182,9 @@ func (d *decoder) canSetAsMapKey(index int) error { } switch { case d.isFixSlice(code), code == def.Array16, code == def.Array32: - return fmt.Errorf("can not use slice code for map key/ code: %x", code) + return fmt.Errorf("%w. code: %x", def.ErrCanNotSetSliceAsMapKey, code) case d.isFixMap(code), code == def.Map16, code == def.Map32: - return fmt.Errorf("can not use map code for map key/ code: %x", code) + return fmt.Errorf("%w. code: %x", def.ErrCanNotSetMapAsMapKey, code) } return nil } diff --git a/internal/decoding/interface_test.go b/internal/decoding/interface_test.go new file mode 100644 index 0000000..c37b3d6 --- /dev/null +++ b/internal/decoding/interface_test.go @@ -0,0 +1,191 @@ +package decoding + +import ( + "fmt" + "reflect" + "testing" + + "github.com/shamaton/msgpack/v2/def" + "github.com/shamaton/msgpack/v2/ext" +) + +func Test_asInterfaceWithCode(t *testing.T) { + dec := testExt2Decoder{} + AddExtDecoder(&dec) + defer RemoveExtDecoder(&dec) + + method := func(d *decoder) func(int, reflect.Kind) (any, int, error) { + return d.asInterface + } + testcases := AsXXXTestCases[any]{ + { + Name: "error.code", + Data: []byte{}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Uint8.error", + Data: []byte{def.Uint8}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Uint16.error", + Data: []byte{def.Uint16}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Uint32.error", + Data: []byte{def.Uint32}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Uint64.error", + Data: []byte{def.Uint64}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Int8.error", + Data: []byte{def.Int8}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Int16.error", + Data: []byte{def.Int16}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Int32.error", + Data: []byte{def.Int32}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Int64.error", + Data: []byte{def.Int64}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Float32.error", + Data: []byte{def.Float32}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Float64.error", + Data: []byte{def.Float64}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Str.error", + Data: []byte{def.Str8}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Bin.error", + Data: []byte{def.Bin8}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Array.error.length", + Data: []byte{def.Array16}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Array.error.required", + Data: []byte{def.Array16, 0, 1}, + Error: def.ErrLackDataLengthToSlice, + MethodAs: method, + }, + { + Name: "Array.error.set", + Data: []byte{def.Array16, 0, 1, def.Int8}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Map.error.length", + Data: []byte{def.Map16}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Map.error.required", + Data: []byte{def.Map16, 0, 1}, + Error: def.ErrLackDataLengthToMap, + MethodAs: method, + }, + { + Name: "Map.error.set.can.slice", + Data: []byte{def.Map16, 0, 1, def.Array16, 0}, + Error: def.ErrCanNotSetSliceAsMapKey, + MethodAs: method, + }, + { + Name: "Map.error.set.can.map", + Data: []byte{def.Map16, 0, 1, def.Map16, 0}, + Error: def.ErrCanNotSetMapAsMapKey, + MethodAs: method, + }, + { + Name: "Map.error.set.key", + Data: []byte{def.Map16, 0, 1, def.Str8, 1}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Map.error.set.value", + Data: []byte{def.Map16, 0, 1, def.FixStr + 1, 'a'}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "ExtCoder.error", + Data: []byte{def.Fixext1, 3}, + Error: ErrTestExtDecoder, + MethodAs: method, + }, + } + + for _, tc := range testcases { + tc.Run(t) + } +} + +// TODO: to testutil +type testExt2Decoder struct { + ext.DecoderCommon +} + +var _ ext.Decoder = (*testExt2Decoder)(nil) + +func (td *testExt2Decoder) Code() int8 { + return 3 +} + +func (td *testExt2Decoder) IsType(o int, d *[]byte) bool { + // todo : lack of error handling + code, _ := td.ReadSize1(o, d) + if code == def.Fixext1 { + extCode, _ := td.ReadSize1(o+1, d) + return int8(extCode) == td.Code() + } + return false +} + +var ErrTestExtDecoder = fmt.Errorf("testExtDecoder") + +func (td *testExt2Decoder) AsValue(_ int, _ reflect.Kind, _ *[]byte) (any, int, error) { + return nil, 0, ErrTestExtDecoder +} diff --git a/internal/decoding/map.go b/internal/decoding/map.go index 2f2d52c..9463700 100644 --- a/internal/decoding/map.go +++ b/internal/decoding/map.go @@ -2,7 +2,6 @@ package decoding import ( "encoding/binary" - "errors" "reflect" "github.com/shamaton/msgpack/v2/def" @@ -88,7 +87,7 @@ func (d *decoder) mapLength(offset int, k reflect.Kind) (int, int, error) { func (d *decoder) hasRequiredLeastMapSize(offset, length int) error { // minimum check (byte length) if len(d.data[offset:]) < length*2 { - return errors.New("data length lacks to create map") + return def.ErrLackDataLengthToMap } return nil } diff --git a/internal/decoding/map_test.go b/internal/decoding/map_test.go new file mode 100644 index 0000000..21d5ab9 --- /dev/null +++ b/internal/decoding/map_test.go @@ -0,0 +1,561 @@ +package decoding + +import ( + "fmt" + "math" + "reflect" + "testing" + + "github.com/shamaton/msgpack/v2/def" + tu "github.com/shamaton/msgpack/v2/internal/common/testutil" +) + +func Test_mapLength(t *testing.T) { + method := func(d *decoder) func(int, reflect.Kind) (int, int, error) { + return d.mapLength + } + testcases := AsXXXTestCases[int]{ + { + Name: "error.code", + Data: []byte{}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "FixMap", + Data: []byte{def.FixMap + 3}, + Expected: 3, + MethodAs: method, + }, + { + Name: "Map16.error", + Data: []byte{def.Map16}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Map16.ok", + Data: []byte{def.Map16, 0xff, 0xff}, + Expected: math.MaxUint16, + MethodAs: method, + }, + { + Name: "Map32.error", + Data: []byte{def.Map32}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Map32.ok", + Data: []byte{def.Map32, 0xff, 0xff, 0xff, 0xff}, + Expected: math.MaxUint32, + MethodAs: method, + }, + { + Name: "Unexpected", + Data: []byte{def.Nil}, + Error: def.ErrCanNotDecode, + MethodAs: method, + }, + } + testcases.Run(t) +} + +func Test_asFixedMap_StringInt(t *testing.T) { + run := func(t *testing.T, v any, dv byte) { + method := func(d *decoder) (int, bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedMap(rv.Elem(), 0, 1) + } + + name := fmt.Sprintf("%T", v) + testcases := AsXXXTestCases[bool]{ + { + Name: name + ".error.asString", + Data: []byte{def.Int32}, + Expected: false, + Error: def.ErrCanNotDecode, + MethodAsCustom: method, + }, + { + Name: name + ".error.asInt", + Data: []byte{def.FixStr + 1, 'a', def.Str8}, + Expected: false, + Error: def.ErrCanNotDecode, + MethodAsCustom: method, + }, + { + Name: name + ".ok", + Data: []byte{def.FixStr + 1, 'a', def.PositiveFixIntMin + dv}, + Expected: true, + MethodAsCustom: method, + }, + } + testcases.Run(t) + } + + v1 := new(map[string]int) + run(t, v1, 1) + tu.EqualMap(t, *v1, map[string]int{"a": 1}) + v2 := new(map[string]int8) + run(t, v2, 2) + tu.EqualMap(t, *v2, map[string]int8{"a": 2}) + v3 := new(map[string]int16) + run(t, v3, 3) + tu.EqualMap(t, *v3, map[string]int16{"a": 3}) + v4 := new(map[string]int32) + run(t, v4, 4) + tu.EqualMap(t, *v4, map[string]int32{"a": 4}) + v5 := new(map[string]int64) + run(t, v5, 5) + tu.EqualMap(t, *v5, map[string]int64{"a": 5}) +} + +func Test_asFixedMap_StringUint(t *testing.T) { + run := func(t *testing.T, v any, dv byte) { + method := func(d *decoder) (int, bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedMap(rv.Elem(), 0, 1) + } + + name := fmt.Sprintf("%T", v) + testcases := AsXXXTestCases[bool]{ + { + Name: name + ".error.asString", + Data: []byte{def.Int32}, + Expected: false, + Error: def.ErrCanNotDecode, + MethodAsCustom: method, + }, + { + Name: name + ".error.asUint", + Data: []byte{def.FixStr + 1, 'a', def.Str8}, + Expected: false, + Error: def.ErrCanNotDecode, + MethodAsCustom: method, + }, + { + Name: name + ".ok", + Data: []byte{def.FixStr + 1, 'a', def.Uint8, dv}, + Expected: true, + MethodAsCustom: method, + }, + } + testcases.Run(t) + } + + v1 := new(map[string]uint) + run(t, v1, 1) + tu.EqualMap(t, *v1, map[string]uint{"a": 1}) + v2 := new(map[string]uint8) + run(t, v2, 2) + tu.EqualMap(t, *v2, map[string]uint8{"a": 2}) + v3 := new(map[string]uint16) + run(t, v3, 3) + tu.EqualMap(t, *v3, map[string]uint16{"a": 3}) + v4 := new(map[string]uint32) + run(t, v4, 4) + tu.EqualMap(t, *v4, map[string]uint32{"a": 4}) + v5 := new(map[string]uint64) + run(t, v5, 5) + tu.EqualMap(t, *v5, map[string]uint64{"a": 5}) +} + +func Test_asFixedMap_StringFloat(t *testing.T) { + run := func(t *testing.T, v any, dv byte) { + method := func(d *decoder) (int, bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedMap(rv.Elem(), 0, 1) + } + + name := fmt.Sprintf("%T", v) + testcases := AsXXXTestCases[bool]{ + { + Name: name + ".error.asString", + Data: []byte{def.Int32}, + Expected: false, + Error: def.ErrCanNotDecode, + MethodAsCustom: method, + }, + { + Name: name + ".error.asFloat", + Data: []byte{def.FixStr + 1, 'a', def.Str8}, + Expected: false, + Error: def.ErrCanNotDecode, + MethodAsCustom: method, + }, + { + Name: name + ".ok", + Data: []byte{def.FixStr + 1, 'a', def.Int16, 0, dv}, + Expected: true, + MethodAsCustom: method, + }, + } + testcases.Run(t) + } + + v1 := new(map[string]float32) + run(t, v1, 1) + tu.EqualMap(t, *v1, map[string]float32{"a": 1}) + v2 := new(map[string]float64) + run(t, v2, 2) + tu.EqualMap(t, *v2, map[string]float64{"a": 2}) +} + +func Test_asFixedMap_StringBool(t *testing.T) { + run := func(t *testing.T, v any, dv byte) { + method := func(d *decoder) (int, bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedMap(rv.Elem(), 0, 1) + } + + name := fmt.Sprintf("%T", v) + testcases := AsXXXTestCases[bool]{ + { + Name: name + ".error.asString", + Data: []byte{def.Int32}, + Expected: false, + Error: def.ErrCanNotDecode, + MethodAsCustom: method, + }, + { + Name: name + ".error.asBool", + Data: []byte{def.FixStr + 1, 'a', def.Str8}, + Expected: false, + Error: def.ErrCanNotDecode, + MethodAsCustom: method, + }, + { + Name: name + ".ok", + Data: []byte{def.FixStr + 1, 'a', dv}, + Expected: true, + MethodAsCustom: method, + }, + } + testcases.Run(t) + } + + v1 := new(map[string]bool) + run(t, v1, def.True) + tu.EqualMap(t, *v1, map[string]bool{"a": true}) +} + +func Test_asFixedMap_StringString(t *testing.T) { + run := func(t *testing.T, v any, dv byte) { + method := func(d *decoder) (int, bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedMap(rv.Elem(), 0, 1) + } + + name := fmt.Sprintf("%T", v) + testcases := AsXXXTestCases[bool]{ + { + Name: name + ".error.asString", + Data: []byte{def.Int32}, + Expected: false, + Error: def.ErrCanNotDecode, + MethodAsCustom: method, + }, + { + Name: name + ".error.asString", + Data: []byte{def.FixStr + 1, 'a', def.Int32}, + Expected: false, + Error: def.ErrCanNotDecode, + MethodAsCustom: method, + }, + { + Name: name + ".ok", + Data: []byte{def.FixStr + 1, 'a', def.FixStr + 1, dv}, + Expected: true, + MethodAsCustom: method, + }, + } + testcases.Run(t) + } + + v1 := new(map[string]string) + run(t, v1, 'b') + tu.EqualMap(t, *v1, map[string]string{"a": "b"}) +} + +func Test_asFixedMap_IntString(t *testing.T) { + run := func(t *testing.T, v any, dv byte) { + method := func(d *decoder) (int, bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedMap(rv.Elem(), 0, 1) + } + + name := fmt.Sprintf("%T", v) + testcases := AsXXXTestCases[bool]{ + { + Name: name + ".error.asInt", + Data: []byte{def.Str8}, + Expected: false, + Error: def.ErrCanNotDecode, + MethodAsCustom: method, + }, + { + Name: name + ".error.asString", + Data: []byte{def.Int8, dv, def.Int32}, + Expected: false, + Error: def.ErrCanNotDecode, + MethodAsCustom: method, + }, + { + Name: name + ".ok", + Data: []byte{def.Int8, dv, def.FixStr + 1, 'b'}, + Expected: true, + MethodAsCustom: method, + }, + } + testcases.Run(t) + } + + v1 := new(map[int]string) + run(t, v1, 1) + tu.EqualMap(t, *v1, map[int]string{1: "b"}) + v2 := new(map[int8]string) + run(t, v2, 2) + tu.EqualMap(t, *v2, map[int8]string{int8(2): "b"}) + v3 := new(map[int16]string) + run(t, v3, 3) + tu.EqualMap(t, *v3, map[int16]string{int16(3): "b"}) + v4 := new(map[int32]string) + run(t, v4, 4) + tu.EqualMap(t, *v4, map[int32]string{int32(4): "b"}) + v5 := new(map[int64]string) + run(t, v5, 5) + tu.EqualMap(t, *v5, map[int64]string{int64(5): "b"}) +} + +func Test_asFixedMap_IntBool(t *testing.T) { + run := func(t *testing.T, v any, dv byte) { + method := func(d *decoder) (int, bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedMap(rv.Elem(), 0, 1) + } + + name := fmt.Sprintf("%T", v) + testcases := AsXXXTestCases[bool]{ + { + Name: name + ".error.asInt", + Data: []byte{def.Str8}, + Expected: false, + Error: def.ErrCanNotDecode, + MethodAsCustom: method, + }, + { + Name: name + ".error.asBool", + Data: []byte{def.Int8, dv, def.Int32}, + Expected: false, + Error: def.ErrCanNotDecode, + MethodAsCustom: method, + }, + { + Name: name + ".ok", + Data: []byte{def.Int8, dv, def.True}, + Expected: true, + MethodAsCustom: method, + }, + } + testcases.Run(t) + } + + v1 := new(map[int]bool) + run(t, v1, 1) + tu.EqualMap(t, *v1, map[int]bool{1: true}) + v2 := new(map[int8]bool) + run(t, v2, 2) + tu.EqualMap(t, *v2, map[int8]bool{int8(2): true}) + v3 := new(map[int16]bool) + run(t, v3, 3) + tu.EqualMap(t, *v3, map[int16]bool{int16(3): true}) + v4 := new(map[int32]bool) + run(t, v4, 4) + tu.EqualMap(t, *v4, map[int32]bool{int32(4): true}) + v5 := new(map[int64]bool) + run(t, v5, 5) + tu.EqualMap(t, *v5, map[int64]bool{int64(5): true}) +} + +func Test_asFixedMap_UintString(t *testing.T) { + run := func(t *testing.T, v any, dv byte) { + method := func(d *decoder) (int, bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedMap(rv.Elem(), 0, 1) + } + + name := fmt.Sprintf("%T", v) + testcases := AsXXXTestCases[bool]{ + { + Name: name + ".error.asUint", + Data: []byte{def.Str8}, + Expected: false, + Error: def.ErrCanNotDecode, + MethodAsCustom: method, + }, + { + Name: name + ".error.asString", + Data: []byte{def.Uint8, dv, def.Int32}, + Expected: false, + Error: def.ErrCanNotDecode, + MethodAsCustom: method, + }, + { + Name: name + ".ok", + Data: []byte{def.Uint8, dv, def.FixStr + 1, 'b'}, + Expected: true, + MethodAsCustom: method, + }, + } + testcases.Run(t) + } + + v1 := new(map[uint]string) + run(t, v1, 1) + tu.EqualMap(t, *v1, map[uint]string{1: "b"}) + v2 := new(map[uint8]string) + run(t, v2, 2) + tu.EqualMap(t, *v2, map[uint8]string{uint8(2): "b"}) + v3 := new(map[uint16]string) + run(t, v3, 3) + tu.EqualMap(t, *v3, map[uint16]string{uint16(3): "b"}) + v4 := new(map[uint32]string) + run(t, v4, 4) + tu.EqualMap(t, *v4, map[uint32]string{uint32(4): "b"}) + v5 := new(map[uint64]string) + run(t, v5, 5) + tu.EqualMap(t, *v5, map[uint64]string{uint64(5): "b"}) +} + +func Test_asFixedMap_UintBool(t *testing.T) { + run := func(t *testing.T, v any, dv byte) { + method := func(d *decoder) (int, bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedMap(rv.Elem(), 0, 1) + } + + name := fmt.Sprintf("%T", v) + testcases := AsXXXTestCases[bool]{ + { + Name: name + ".error.asUint", + Data: []byte{def.Str8}, + Expected: false, + Error: def.ErrCanNotDecode, + MethodAsCustom: method, + }, + { + Name: name + ".error.asBool", + Data: []byte{def.Uint8, dv, def.Int32}, + Expected: false, + Error: def.ErrCanNotDecode, + MethodAsCustom: method, + }, + { + Name: name + ".ok", + Data: []byte{def.Uint8, dv, def.True}, + Expected: true, + MethodAsCustom: method, + }, + } + testcases.Run(t) + } + + v1 := new(map[uint]bool) + run(t, v1, 1) + tu.EqualMap(t, *v1, map[uint]bool{1: true}) + v2 := new(map[uint8]bool) + run(t, v2, 2) + tu.EqualMap(t, *v2, map[uint8]bool{uint8(2): true}) + v3 := new(map[uint16]bool) + run(t, v3, 3) + tu.EqualMap(t, *v3, map[uint16]bool{uint16(3): true}) + v4 := new(map[uint32]bool) + run(t, v4, 4) + tu.EqualMap(t, *v4, map[uint32]bool{uint32(4): true}) + v5 := new(map[uint64]bool) + run(t, v5, 5) + tu.EqualMap(t, *v5, map[uint64]bool{uint64(5): true}) +} + +func Test_asFixedMap_FloatString(t *testing.T) { + run := func(t *testing.T, v any, dv byte) { + method := func(d *decoder) (int, bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedMap(rv.Elem(), 0, 1) + } + + name := fmt.Sprintf("%T", v) + testcases := AsXXXTestCases[bool]{ + { + Name: name + ".error.asFloat", + Data: []byte{def.Str8}, + Expected: false, + Error: def.ErrCanNotDecode, + MethodAsCustom: method, + }, + { + Name: name + ".error.asString", + Data: []byte{def.Uint8, dv, def.Int32}, + Expected: false, + Error: def.ErrCanNotDecode, + MethodAsCustom: method, + }, + { + Name: name + ".ok", + Data: []byte{def.Uint8, dv, def.FixStr + 1, 'b'}, + Expected: true, + MethodAsCustom: method, + }, + } + testcases.Run(t) + } + + v1 := new(map[float32]string) + run(t, v1, 1) + tu.EqualMap(t, *v1, map[float32]string{1: "b"}) + v2 := new(map[float64]string) + run(t, v2, 2) + tu.EqualMap(t, *v2, map[float64]string{2: "b"}) +} + +func Test_asFixedMap_FloatBool(t *testing.T) { + run := func(t *testing.T, v any, dv byte) { + method := func(d *decoder) (int, bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedMap(rv.Elem(), 0, 1) + } + + name := fmt.Sprintf("%T", v) + testcases := AsXXXTestCases[bool]{ + { + Name: name + ".error.asFloat", + Data: []byte{def.Str8}, + Expected: false, + Error: def.ErrCanNotDecode, + MethodAsCustom: method, + }, + { + Name: name + ".error.asBool", + Data: []byte{def.Uint8, dv, def.Int32}, + Expected: false, + Error: def.ErrCanNotDecode, + MethodAsCustom: method, + }, + { + Name: name + ".ok", + Data: []byte{def.Uint8, dv, def.True}, + Expected: true, + MethodAsCustom: method, + }, + } + testcases.Run(t) + } + + v1 := new(map[float32]bool) + run(t, v1, 1) + tu.EqualMap(t, *v1, map[float32]bool{1: true}) + v2 := new(map[float64]bool) + run(t, v2, 2) + tu.EqualMap(t, *v2, map[float64]bool{2: true}) +} diff --git a/internal/decoding/slice.go b/internal/decoding/slice.go index 12a86fa..fa4b1aa 100644 --- a/internal/decoding/slice.go +++ b/internal/decoding/slice.go @@ -2,7 +2,6 @@ package decoding import ( "encoding/binary" - "errors" "reflect" "github.com/shamaton/msgpack/v2/def" @@ -61,7 +60,7 @@ func (d *decoder) sliceLength(offset int, k reflect.Kind) (int, int, error) { func (d *decoder) hasRequiredLeastSliceSize(offset, length int) error { // minimum check (byte length) if len(d.data[offset:]) < length { - return errors.New("data length lacks to create map") + return def.ErrLackDataLengthToSlice } return nil } diff --git a/internal/decoding/slice_test.go b/internal/decoding/slice_test.go new file mode 100644 index 0000000..66fb0bb --- /dev/null +++ b/internal/decoding/slice_test.go @@ -0,0 +1,469 @@ +package decoding + +import ( + "math" + "reflect" + "testing" + + "github.com/shamaton/msgpack/v2/def" + tu "github.com/shamaton/msgpack/v2/internal/common/testutil" +) + +func Test_sliceLength(t *testing.T) { + method := func(d *decoder) func(int, reflect.Kind) (int, int, error) { + return d.sliceLength + } + testcases := AsXXXTestCases[int]{ + { + Name: "error.code", + Data: []byte{}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "FixArray", + Data: []byte{def.FixArray + 3}, + Expected: 3, + MethodAs: method, + }, + { + Name: "Array16.error", + Data: []byte{def.Array16}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Array16.ok", + Data: []byte{def.Array16, 0xff, 0xff}, + Expected: math.MaxUint16, + MethodAs: method, + }, + { + Name: "Array32.error", + Data: []byte{def.Array32}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Array32.ok", + Data: []byte{def.Array32, 0xff, 0xff, 0xff, 0xff}, + Expected: math.MaxUint32, + MethodAs: method, + }, + { + Name: "Unexpected", + Data: []byte{def.Nil}, + Error: def.ErrCanNotDecode, + MethodAs: method, + }, + } + testcases.Run(t) +} + +func Test_asFixedSlice_Int(t *testing.T) { + run := func(t *testing.T, v any) { + method := func(d *decoder) (int, bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedSlice(rv.Elem(), 0, 1) + } + + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{}, + Error: def.ErrTooShortBytes, + MethodAsCustom: method, + }, + { + Name: "ok", + Data: []byte{def.PositiveFixIntMin + 3}, + Expected: true, + MethodAsCustom: method, + }, + } + testcases.Run(t) + } + + v1 := new([]int) + run(t, v1) + tu.EqualSlice(t, *v1, []int{3}) +} + +func Test_asFixedSlice_Int8(t *testing.T) { + run := func(t *testing.T, v any) { + method := func(d *decoder) (int, bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedSlice(rv.Elem(), 0, 1) + } + + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{}, + Error: def.ErrTooShortBytes, + MethodAsCustom: method, + }, + { + Name: "ok", + Data: []byte{def.PositiveFixIntMin + 4}, + Expected: true, + MethodAsCustom: method, + }, + } + for _, tc := range testcases { + tc.Run(t) + } + } + + v1 := new([]int8) + run(t, v1) + tu.EqualSlice(t, *v1, []int8{4}) +} + +func Test_asFixedSlice_Int16(t *testing.T) { + run := func(t *testing.T, v any) { + method := func(d *decoder) (int, bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedSlice(rv.Elem(), 0, 1) + } + + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{}, + Error: def.ErrTooShortBytes, + MethodAsCustom: method, + }, + { + Name: "ok", + Data: []byte{def.PositiveFixIntMin + 5}, + Expected: true, + MethodAsCustom: method, + }, + } + testcases.Run(t) + } + + v1 := new([]int16) + run(t, v1) + tu.EqualSlice(t, *v1, []int16{5}) +} + +func Test_asFixedSlice_Int32(t *testing.T) { + run := func(t *testing.T, v any) { + method := func(d *decoder) (int, bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedSlice(rv.Elem(), 0, 1) + } + + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{}, + Error: def.ErrTooShortBytes, + MethodAsCustom: method, + }, + { + Name: "ok", + Data: []byte{def.PositiveFixIntMin + 6}, + Expected: true, + MethodAsCustom: method, + }, + } + testcases.Run(t) + } + + v1 := new([]int32) + run(t, v1) + tu.EqualSlice(t, *v1, []int32{6}) +} + +func Test_asFixedSlice_Int64(t *testing.T) { + run := func(t *testing.T, v any) { + method := func(d *decoder) (int, bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedSlice(rv.Elem(), 0, 1) + } + + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{}, + Error: def.ErrTooShortBytes, + MethodAsCustom: method, + }, + { + Name: "ok", + Data: []byte{def.PositiveFixIntMin + 7}, + Expected: true, + MethodAsCustom: method, + }, + } + testcases.Run(t) + } + + v1 := new([]int64) + run(t, v1) + tu.EqualSlice(t, *v1, []int64{7}) +} + +func Test_asFixedSlice_Uint(t *testing.T) { + run := func(t *testing.T, v any) { + method := func(d *decoder) (int, bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedSlice(rv.Elem(), 0, 1) + } + + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{}, + Error: def.ErrTooShortBytes, + MethodAsCustom: method, + }, + { + Name: "ok", + Data: []byte{def.PositiveFixIntMin + 5}, + Expected: true, + MethodAsCustom: method, + }, + } + testcases.Run(t) + } + + v1 := new([]uint) + run(t, v1) + tu.EqualSlice(t, *v1, []uint{5}) +} + +func Test_asFixedSlice_Uint8(t *testing.T) { + run := func(t *testing.T, v any) { + method := func(d *decoder) (int, bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedSlice(rv.Elem(), 0, 1) + } + + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{}, + Error: def.ErrTooShortBytes, + MethodAsCustom: method, + }, + { + Name: "ok", + Data: []byte{def.PositiveFixIntMin + 6}, + Expected: true, + MethodAsCustom: method, + }, + } + testcases.Run(t) + } + + v1 := new([]uint8) + run(t, v1) + tu.EqualSlice(t, *v1, []uint8{6}) +} + +func Test_asFixedSlice_Uint16(t *testing.T) { + run := func(t *testing.T, v any) { + method := func(d *decoder) (int, bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedSlice(rv.Elem(), 0, 1) + } + + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{}, + Error: def.ErrTooShortBytes, + MethodAsCustom: method, + }, + { + Name: "ok", + Data: []byte{def.PositiveFixIntMin + 7}, + Expected: true, + MethodAsCustom: method, + }, + } + testcases.Run(t) + } + + v1 := new([]uint16) + run(t, v1) + tu.EqualSlice(t, *v1, []uint16{7}) +} + +func Test_asFixedSlice_Uint32(t *testing.T) { + run := func(t *testing.T, v any) { + method := func(d *decoder) (int, bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedSlice(rv.Elem(), 0, 1) + } + + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{}, + Error: def.ErrTooShortBytes, + MethodAsCustom: method, + }, + { + Name: "ok", + Data: []byte{def.PositiveFixIntMin + 8}, + Expected: true, + MethodAsCustom: method, + }, + } + testcases.Run(t) + } + + v1 := new([]uint32) + run(t, v1) + tu.EqualSlice(t, *v1, []uint32{8}) +} + +func Test_asFixedSlice_Uint64(t *testing.T) { + run := func(t *testing.T, v any) { + method := func(d *decoder) (int, bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedSlice(rv.Elem(), 0, 1) + } + + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{}, + Error: def.ErrTooShortBytes, + MethodAsCustom: method, + }, + { + Name: "ok", + Data: []byte{def.PositiveFixIntMin + 9}, + Expected: true, + MethodAsCustom: method, + }, + } + testcases.Run(t) + } + + v1 := new([]uint64) + run(t, v1) + tu.EqualSlice(t, *v1, []uint64{9}) +} + +func Test_asFixedSlice_Float32(t *testing.T) { + run := func(t *testing.T, v any) { + method := func(d *decoder) (int, bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedSlice(rv.Elem(), 0, 1) + } + + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{}, + Error: def.ErrTooShortBytes, + MethodAsCustom: method, + }, + { + Name: "ok", + Data: []byte{def.Float32, 63, 128, 0, 0}, + Expected: true, + MethodAsCustom: method, + }, + } + testcases.Run(t) + } + + v1 := new([]float32) + run(t, v1) + tu.EqualSlice(t, *v1, []float32{1}) +} + +func Test_asFixedSlice_Float64(t *testing.T) { + run := func(t *testing.T, v any) { + method := func(d *decoder) (int, bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedSlice(rv.Elem(), 0, 1) + } + + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{}, + Error: def.ErrTooShortBytes, + MethodAsCustom: method, + }, + { + Name: "ok", + Data: []byte{def.Float64, 63, 240, 0, 0, 0, 0, 0, 0}, + Expected: true, + MethodAsCustom: method, + }, + } + testcases.Run(t) + } + + v1 := new([]float64) + run(t, v1) + tu.EqualSlice(t, *v1, []float64{1}) +} + +func Test_asFixedSlice_String(t *testing.T) { + run := func(t *testing.T, v any) { + method := func(d *decoder) (int, bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedSlice(rv.Elem(), 0, 2) + } + + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{}, + Error: def.ErrTooShortBytes, + MethodAsCustom: method, + }, + { + Name: "ok", + Data: []byte{def.FixStr + 1, 'a', def.FixStr + 1, 'b'}, + Expected: true, + MethodAsCustom: method, + }, + } + testcases.Run(t) + } + + v1 := new([]string) + run(t, v1) + tu.EqualSlice(t, *v1, []string{"a", "b"}) +} + +func Test_asFixedSlice_Bool(t *testing.T) { + run := func(t *testing.T, v any) { + method := func(d *decoder) (int, bool, error) { + rv := reflect.ValueOf(v) + return d.asFixedSlice(rv.Elem(), 0, 1) + } + + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Data: []byte{}, + Error: def.ErrTooShortBytes, + MethodAsCustom: method, + }, + { + Name: "ok", + Data: []byte{def.True}, + Expected: true, + MethodAsCustom: method, + }, + } + testcases.Run(t) + } + + v1 := new([]bool) + run(t, v1) + tu.EqualSlice(t, *v1, []bool{true}) +} diff --git a/internal/decoding/string_test.go b/internal/decoding/string_test.go new file mode 100644 index 0000000..f15c153 --- /dev/null +++ b/internal/decoding/string_test.go @@ -0,0 +1,121 @@ +package decoding + +import ( + "math" + "reflect" + "testing" + + "github.com/shamaton/msgpack/v2/def" +) + +func Test_stringByteLength(t *testing.T) { + method := func(d *decoder) func(int, reflect.Kind) (int, int, error) { + return d.stringByteLength + } + testcases := AsXXXTestCases[int]{ + { + Name: "error.code", + Data: []byte{}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "FixStr.ok", + Data: []byte{def.FixStr + 1}, + Expected: 1, + MethodAs: method, + }, + { + Name: "Str8.error", + Data: []byte{def.Str8}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Str8.ok", + Data: []byte{def.Str8, 0xff}, + Expected: math.MaxUint8, + MethodAs: method, + }, + { + Name: "Str16.error", + Data: []byte{def.Str16}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Str16.ok", + Data: []byte{def.Str16, 0xff, 0xff}, + Expected: math.MaxUint16, + MethodAs: method, + }, + { + Name: "Str32.error", + Data: []byte{def.Str32}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Str32.ok", + Data: []byte{def.Str32, 0xff, 0xff, 0xff, 0xff}, + Expected: math.MaxUint32, + + MethodAs: method, + }, + { + Name: "Nil", + Data: []byte{def.Nil}, + Expected: 0, + MethodAs: method, + }, + { + Name: "Unexpected", + Data: []byte{def.Array16}, + Error: def.ErrCanNotDecode, + MethodAs: method, + }, + } + testcases.Run(t) +} + +func Test_asString(t *testing.T) { + method := func(d *decoder) func(int, reflect.Kind) (string, int, error) { + return d.asString + } + testcases := AsXXXTestCases[string]{ + { + Name: "error.string", + Data: []byte{def.FixStr + 1}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "ok", + Data: []byte{def.FixStr + 1, 'a'}, + Expected: "a", + MethodAs: method, + }, + } + testcases.Run(t) +} + +func Test_asStringByte(t *testing.T) { + method := func(d *decoder) func(int, reflect.Kind) ([]byte, int, error) { + return d.asStringByte + } + testcases := AsXXXTestCases[[]byte]{ + { + Name: "error", + Data: []byte{def.FixStr + 1}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "ok", + Data: []byte{def.FixStr + 1, 'a'}, + Expected: []byte{'a'}, + MethodAs: method, + }, + } + testcases.Run(t) +} diff --git a/internal/decoding/struct_test.go b/internal/decoding/struct_test.go new file mode 100644 index 0000000..c272dab --- /dev/null +++ b/internal/decoding/struct_test.go @@ -0,0 +1,466 @@ +package decoding + +import ( + "reflect" + "testing" + "time" + + "github.com/shamaton/msgpack/v2/def" + tu "github.com/shamaton/msgpack/v2/internal/common/testutil" +) + +func Test_setStruct_ext(t *testing.T) { + run := func(t *testing.T, rv reflect.Value) { + + method := func(d *decoder) func(int, reflect.Kind) (any, int, error) { + return func(offset int, k reflect.Kind) (any, int, error) { + o, err := d.setStruct(rv, offset, k) + return nil, o, err + } + } + + testcases := AsXXXTestCases[any]{ + { + Name: "ExtCoder.error", + Data: []byte{def.Fixext1, 3, 0}, + Error: ErrTestExtDecoder, + MethodAs: method, + }, + { + Name: "ExtCoder.ok", + Data: []byte{def.Fixext4, 255, 0, 0, 0, 0}, + MethodAs: method, + }, + } + testcases.Run(t) + } + + ngDec := testExt2Decoder{} + AddExtDecoder(&ngDec) + defer RemoveExtDecoder(&ngDec) + + v1 := new(time.Time) + run(t, reflect.ValueOf(v1).Elem()) + tu.EqualEqualer(t, *v1, time.Unix(0, 0)) +} + +func Test_setStructFromMap(t *testing.T) { + run := func(t *testing.T, rv reflect.Value) { + method := func(d *decoder) func(int, reflect.Kind) (any, int, error) { + return func(offset int, k reflect.Kind) (any, int, error) { + o, err := d.setStructFromMap(rv, offset, k) + return nil, o, err + } + } + + testcases := AsXXXTestCases[any]{ + { + Name: "error.length", + Data: []byte{}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "error.required", + Data: []byte{def.Map16, 0, 1}, + Error: def.ErrLackDataLengthToMap, + MethodAs: method, + }, + { + Name: "error.key", + Data: []byte{def.Map16, 0, 1, def.Str16, 0}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "error.decode", + Data: []byte{def.Map16, 0, 1, def.FixStr + 1, 'v', def.Array16}, + Error: def.ErrCanNotDecode, + MethodAs: method, + }, + { + Name: "error.jump", + Data: []byte{def.Map16, 0, 2, def.FixStr + 1, 'v', 0, def.FixStr + 1, 'b'}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "ok", + Data: []byte{def.Map16, 0, 1, def.FixStr + 1, 'v', def.PositiveFixIntMin + 7}, + MethodAs: method, + }, + } + testcases.Run(t) + } + + type st struct { + V int `msgpack:"v"` + } + + v1 := new(st) + run(t, reflect.ValueOf(v1).Elem()) + tu.Equal(t, v1.V, 7) +} + +func Test_setStructFromArray(t *testing.T) { + run := func(t *testing.T, rv reflect.Value) { + method := func(d *decoder) func(int, reflect.Kind) (any, int, error) { + return func(offset int, k reflect.Kind) (any, int, error) { + o, err := d.setStructFromArray(rv, offset, k) + return nil, o, err + } + } + + testcases := AsXXXTestCases[any]{ + { + Name: "error.length", + Data: []byte{}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "error.required", + Data: []byte{def.Array16, 0, 1}, + Error: def.ErrLackDataLengthToSlice, + MethodAs: method, + }, + { + Name: "error.decode", + Data: []byte{def.Array16, 0, 1, def.Array16}, + Error: def.ErrCanNotDecode, + MethodAs: method, + }, + { + Name: "error.jump", + Data: []byte{def.Array16, 0, 2, 0, def.Array16}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "ok", + Data: []byte{def.Array16, 0, 1, def.PositiveFixIntMin + 8}, + MethodAs: method, + }, + } + testcases.Run(t) + } + + type st struct { + V int `msgpack:"v"` + } + + v1 := new(st) + run(t, reflect.ValueOf(v1).Elem()) + tu.Equal(t, v1.V, 8) +} + +func Test_jumpOffset(t *testing.T) { + method := func(d *decoder) func(int, reflect.Kind) (any, int, error) { + return func(offset int, _ reflect.Kind) (any, int, error) { + o, err := d.jumpOffset(offset) + return nil, o, err + } + } + + testcases := AsXXXTestCases[any]{ + { + Name: "error.read.code", + Data: []byte{}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "True.ok", + Data: []byte{def.True}, + MethodAs: method, + }, + { + Name: "False.ok", + Data: []byte{def.False}, + MethodAs: method, + }, + { + Name: "PositiveFixNum.ok", + Data: []byte{def.PositiveFixIntMin + 1}, + MethodAs: method, + }, + { + Name: "NegativeFixNum.ok", + Data: []byte{0xf0}, + MethodAs: method, + }, + { + Name: "Uint8.ok", + Data: []byte{def.Uint8, 1}, + MethodAs: method, + }, + { + Name: "Int8.ok", + Data: []byte{def.Int8, 1}, + MethodAs: method, + }, + { + Name: "Uint16.ok", + Data: []byte{def.Uint16, 0, 1}, + MethodAs: method, + }, + { + Name: "Int16.ok", + Data: []byte{def.Int16, 0, 1}, + MethodAs: method, + }, + { + Name: "Uint32.ok", + Data: []byte{def.Uint32, 0, 0, 0, 0}, + MethodAs: method, + }, + { + Name: "Int32.ok", + Data: []byte{def.Int32, 0, 0, 0, 0}, + MethodAs: method, + }, + { + Name: "Float32.ok", + Data: []byte{def.Float32, 0, 0, 0, 0}, + MethodAs: method, + }, + { + Name: "Uint64.ok", + Data: []byte{def.Uint64, 0, 0, 0, 0, 0, 0, 0, 0}, + MethodAs: method, + }, + { + Name: "Int64.ok", + Data: []byte{def.Int64, 0, 0, 0, 0, 0, 0, 0, 0}, + MethodAs: method, + }, + { + Name: "Float64.ok", + Data: []byte{def.Float64, 0, 0, 0, 0, 0, 0, 0, 0}, + MethodAs: method, + }, + { + Name: "FixStr.ok", + Data: []byte{def.FixStr + 1, 0}, + MethodAs: method, + }, + { + Name: "Str8.ng.length", + Data: []byte{def.Str8}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Str8.ok", + Data: []byte{def.Str8, 1, 'a'}, + MethodAs: method, + }, + { + Name: "Bin8.ng.length", + Data: []byte{def.Bin8}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Bin8.ok", + Data: []byte{def.Bin8, 1, 'a'}, + MethodAs: method, + }, + { + Name: "Str16.ng.length", + Data: []byte{def.Str16}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Str16.ok", + Data: []byte{def.Str16, 0, 1, 'a'}, + MethodAs: method, + }, + { + Name: "Bin16.ng.length", + Data: []byte{def.Bin16}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Bin16.ok", + Data: []byte{def.Bin16, 0, 1, 'a'}, + MethodAs: method, + }, + { + Name: "Str32.ng.length", + Data: []byte{def.Str32}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Str32.ok", + Data: []byte{def.Str32, 0, 0, 0, 1, 'a'}, + MethodAs: method, + }, + { + Name: "Bin32.ng.length", + Data: []byte{def.Bin32}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Bin32.ok", + Data: []byte{def.Bin32, 0, 0, 0, 1, 'a'}, + MethodAs: method, + }, + { + Name: "FixSlice.ng", + Data: []byte{def.FixArray + 1}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "FixSlice.ok", + Data: []byte{def.FixArray + 1, 0xc1}, + MethodAs: method, + }, + { + Name: "Array16.ng.len", + Data: []byte{def.Array16}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Array16.ng.jump", + Data: []byte{def.Array16, 0, 1}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Array16.ok", + Data: []byte{def.Array16, 0, 1, 0xc1}, + MethodAs: method, + }, { + Name: "Array32.ng.len", + Data: []byte{def.Array32}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Array32.ng.jump", + Data: []byte{def.Array32, 0, 0, 0, 1}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Array32.ok", + Data: []byte{def.Array32, 0, 0, 0, 1, 0xc1}, + MethodAs: method, + }, + { + Name: "FixMap.ng", + Data: []byte{def.FixMap + 1}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "FixMap.ok", + Data: []byte{def.FixMap + 1, 0xc1, 0xc1}, + MethodAs: method, + }, + { + Name: "Map16.ng.len", + Data: []byte{def.Map16}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Map16.ng.jump", + Data: []byte{def.Map16, 0, 1}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Map16.ok", + Data: []byte{def.Map16, 0, 1, 0xc1, 0xc1}, + MethodAs: method, + }, { + Name: "Map32.ng.len", + Data: []byte{def.Map32}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Map32.ng.jump", + Data: []byte{def.Map32, 0, 0, 0, 1}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Map32.ok", + Data: []byte{def.Map32, 0, 0, 0, 1, 0xc1, 0xc1}, + MethodAs: method, + }, + { + Name: "Fixext1.ok", + Data: []byte{def.Fixext1, 0, 0}, + MethodAs: method, + }, + { + Name: "Fixext2.ok", + Data: []byte{def.Fixext2, 0, 0, 0}, + MethodAs: method, + }, + { + Name: "Fixext4.ok", + Data: []byte{def.Fixext4, 0, 0, 0, 0, 0}, + MethodAs: method, + }, + { + Name: "Fixext8.ok", + Data: []byte{def.Fixext8, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + MethodAs: method, + }, + { + Name: "Fixext16.ok", + Data: []byte{def.Fixext16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + MethodAs: method, + }, + { + Name: "Ext8.ng.size", + Data: []byte{def.Ext8}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Ext8.ok", + Data: []byte{def.Ext8, 1, 0, 0}, + MethodAs: method, + }, + { + Name: "Ext16.ng.size", + Data: []byte{def.Ext16}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Ext16.ok", + Data: []byte{def.Ext16, 0, 1, 0, 0}, + MethodAs: method, + }, + { + Name: "Ext32.ng.size", + Data: []byte{def.Ext32}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Ext32.ok", + Data: []byte{def.Ext32, 0, 0, 0, 1, 0, 0}, + MethodAs: method, + }, + { + Name: "Unexpected", + Data: []byte{0xc1}, + MethodAs: method, + }, + } + testcases.Run(t) +} diff --git a/internal/decoding/uint_test.go b/internal/decoding/uint_test.go new file mode 100644 index 0000000..d16705e --- /dev/null +++ b/internal/decoding/uint_test.go @@ -0,0 +1,144 @@ +package decoding + +import ( + "math" + "reflect" + "testing" + + "github.com/shamaton/msgpack/v2/def" +) + +func Test_asUint(t *testing.T) { + method := func(d *decoder) func(int, reflect.Kind) (uint64, int, error) { + return d.asUint + } + testcases := AsXXXTestCases[uint64]{ + { + Name: "error.code", + Data: []byte{}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "PositiveFixNum.ok", + Data: []byte{def.PositiveFixIntMin + 1}, + Expected: uint64(1), + MethodAs: method, + }, + { + Name: "Uint8.error", + Data: []byte{def.Uint8}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Uint8.ok", + Data: []byte{def.Uint8, 1}, + Expected: uint64(1), + MethodAs: method, + }, + { + Name: "Uint16.error", + Data: []byte{def.Uint16}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Uint16.ok", + Data: []byte{def.Uint16, 0, 1}, + Expected: uint64(1), + MethodAs: method, + }, + { + Name: "Uint32.error", + Data: []byte{def.Uint32}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Uint32.ok", + Data: []byte{def.Uint32, 0, 0, 0, 1}, + Expected: uint64(1), + MethodAs: method, + }, + { + Name: "Uint64.error", + Data: []byte{def.Uint64}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Uint64.ok", + Data: []byte{def.Uint64, 0, 0, 0, 0, 0, 0, 0, 1}, + Expected: uint64(1), + MethodAs: method, + }, + { + Name: "NegativeFixNum.ok", + Data: []byte{0xff}, + Expected: uint64(math.MaxUint64), + MethodAs: method, + }, + { + Name: "Int8.error", + Data: []byte{def.Int8}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Int8.ok", + Data: []byte{def.Int8, 0xff}, + Expected: uint64(math.MaxUint64), + MethodAs: method, + }, + { + Name: "Int16.error", + Data: []byte{def.Int16}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Int16.ok", + Data: []byte{def.Int16, 0xff, 0xff}, + Expected: uint64(math.MaxUint64), + MethodAs: method, + }, + { + Name: "Int32.error", + Data: []byte{def.Int32}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Int32.ok", + Data: []byte{def.Int32, 0xff, 0xff, 0xff, 0xff}, + Expected: uint64(math.MaxUint64), + MethodAs: method, + }, + { + Name: "Int64.error", + Data: []byte{def.Int64}, + Error: def.ErrTooShortBytes, + MethodAs: method, + }, + { + Name: "Int64.ok", + Data: []byte{def.Int64, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + Expected: uint64(math.MaxUint64), + MethodAs: method, + }, + { + Name: "Nil.ok", + Data: []byte{def.Nil}, + Expected: uint64(0), + MethodAs: method, + }, + { + Name: "Unexpected", + Data: []byte{def.Str8}, + Error: def.ErrCanNotDecode, + MethodAs: method, + }, + } + testcases.Run(t) +} From 9f8997d59a3a41bf14cc19fe2a8d88f1be6f73ee Mon Sep 17 00:00:00 2001 From: shamaton Date: Thu, 15 Aug 2024 21:35:34 +0900 Subject: [PATCH 26/41] fix test cases --- def/error.go | 3 +++ internal/decoding/complex_test.go | 4 ++-- internal/decoding/decoding.go | 6 +++--- internal/decoding/decoding_test.go | 13 +++++++++++-- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/def/error.go b/def/error.go index f5b2fea..4e653d0 100644 --- a/def/error.go +++ b/def/error.go @@ -3,6 +3,9 @@ package def import "errors" var ( + ErrNoData = errors.New("no data") + ErrHasLeftOver = errors.New("data has left over") + ErrReceiverNotPointer = errors.New("receiver not pointer") ErrNotMatchArrayElement = errors.New("not match array element") ErrCanNotDecode = errors.New("msgpack : invalid code") ErrCanNotSetSliceAsMapKey = errors.New("can not set slice as map key") diff --git a/internal/decoding/complex_test.go b/internal/decoding/complex_test.go index 61bcdd7..87af7e0 100644 --- a/internal/decoding/complex_test.go +++ b/internal/decoding/complex_test.go @@ -56,7 +56,7 @@ func Test_asComplex64(t *testing.T) { }, { Name: "Fixext16.error.i", - Data: []byte{def.Fixext16, byte(def.ComplexTypeCode()), 0, 0, 0, 1}, + Data: []byte{def.Fixext16, byte(def.ComplexTypeCode()), 0, 0, 0, 0, 0, 0, 0, 1}, Error: def.ErrTooShortBytes, MethodAs: method, }, @@ -126,7 +126,7 @@ func Test_asComplex128(t *testing.T) { }, { Name: "Fixext16.error.i", - Data: []byte{def.Fixext16, byte(def.ComplexTypeCode()), 0, 0, 0, 1}, + Data: []byte{def.Fixext16, byte(def.ComplexTypeCode()), 0, 0, 0, 0, 0, 0, 0, 1}, Error: def.ErrTooShortBytes, MethodAs: method, }, diff --git a/internal/decoding/decoding.go b/internal/decoding/decoding.go index 393f0c3..7d987ee 100644 --- a/internal/decoding/decoding.go +++ b/internal/decoding/decoding.go @@ -20,11 +20,11 @@ func Decode(data []byte, v interface{}, asArray bool) error { d := decoder{data: data, asArray: asArray} if d.data == nil || len(d.data) < 1 { - return fmt.Errorf("data is empty") + return def.ErrNoData } rv := reflect.ValueOf(v) if rv.Kind() != reflect.Ptr { - return fmt.Errorf("holder must set pointer value. but got: %t", v) + return fmt.Errorf("%w. v.(type): %t", def.ErrReceiverNotPointer, v) } rv = rv.Elem() @@ -34,7 +34,7 @@ func Decode(data []byte, v interface{}, asArray bool) error { return err } if len(data) != last { - return fmt.Errorf("failed deserialization size=%d, last=%d", len(data), last) + return fmt.Errorf("%w size=%d, last=%d", def.ErrHasLeftOver, len(data), last) } return err } diff --git a/internal/decoding/decoding_test.go b/internal/decoding/decoding_test.go index ec7a564..3c78427 100644 --- a/internal/decoding/decoding_test.go +++ b/internal/decoding/decoding_test.go @@ -64,8 +64,17 @@ func TestDecoding(t *testing.T) { t.Run("empty data", func(t *testing.T) { v := new(int) err := Decode(nil, v, false) - tu.Error(t, err) - tu.Equal(t, err.Error(), "data is empty") + tu.IsError(t, err, def.ErrNoData) + }) + t.Run("not pointer", func(t *testing.T) { + v := 0 + err := Decode([]byte{def.PositiveFixIntMax}, v, false) + tu.IsError(t, err, def.ErrReceiverNotPointer) + }) + t.Run("left data", func(t *testing.T) { + v := new(int) + err := Decode([]byte{def.PositiveFixIntMin, 0}, v, false) + tu.IsError(t, err, def.ErrHasLeftOver) }) } From d79d1121882745d5885f2688f0fc558841a5c96c Mon Sep 17 00:00:00 2001 From: shamaton Date: Thu, 15 Aug 2024 22:53:42 +0900 Subject: [PATCH 27/41] use error definitions --- internal/decoding/decoding.go | 2 +- internal/stream/decoding/decoding.go | 4 ++-- internal/stream/decoding/decoding_test.go | 9 +++++++-- msgpack_test.go | 2 +- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/internal/decoding/decoding.go b/internal/decoding/decoding.go index 7d987ee..3c03775 100644 --- a/internal/decoding/decoding.go +++ b/internal/decoding/decoding.go @@ -24,7 +24,7 @@ func Decode(data []byte, v interface{}, asArray bool) error { } rv := reflect.ValueOf(v) if rv.Kind() != reflect.Ptr { - return fmt.Errorf("%w. v.(type): %t", def.ErrReceiverNotPointer, v) + return fmt.Errorf("%w. v.(type): %T", def.ErrReceiverNotPointer, v) } rv = rv.Elem() diff --git a/internal/stream/decoding/decoding.go b/internal/stream/decoding/decoding.go index d86de40..4e82e36 100644 --- a/internal/stream/decoding/decoding.go +++ b/internal/stream/decoding/decoding.go @@ -20,11 +20,11 @@ type decoder struct { // the result into the pointer of v. func Decode(r io.Reader, v interface{}, asArray bool) error { if r == nil { - return fmt.Errorf("reader is nil") + return def.ErrNoData } rv := reflect.ValueOf(v) if rv.Kind() != reflect.Ptr { - return fmt.Errorf("holder must set pointer value. but got: %t", v) + return fmt.Errorf("%w. v.(type): %T", def.ErrReceiverNotPointer, v) } rv = rv.Elem() diff --git a/internal/stream/decoding/decoding_test.go b/internal/stream/decoding/decoding_test.go index 741824a..2c3ae22 100644 --- a/internal/stream/decoding/decoding_test.go +++ b/internal/stream/decoding/decoding_test.go @@ -87,8 +87,13 @@ func TestDecoding(t *testing.T) { t.Run("nil reader", func(t *testing.T) { v := new(int) err := Decode(nil, v, false) - tu.Error(t, err) - tu.Equal(t, err.Error(), "reader is nil") + tu.IsError(t, err, def.ErrNoData) + }) + t.Run("not pointer", func(t *testing.T) { + v := 0 + r := tu.NewTestReader([]byte{def.PositiveFixIntMax}) + err := Decode(r, v, false) + tu.IsError(t, err, def.ErrReceiverNotPointer) }) } diff --git a/msgpack_test.go b/msgpack_test.go index f5a7cb3..f2ea867 100644 --- a/msgpack_test.go +++ b/msgpack_test.go @@ -1496,7 +1496,7 @@ func TestPointer(t *testing.T) { var r int t.Run(u.name, func(t *testing.T) { err := u.u([]byte{def.Nil}, r) - ErrorContains(t, err, "holder must set pointer value. but got:") + ErrorContains(t, err, "receiver not pointer") }) } }) From 8c4806091b5ff227adac55d6cee926ef5fe1cd53 Mon Sep 17 00:00:00 2001 From: shamaton Date: Thu, 22 Aug 2024 22:45:14 +0900 Subject: [PATCH 28/41] add encoding bool, int tests --- internal/common/testutil/testutil.go | 14 ++- internal/stream/encoding/bool_test.go | 30 +++++ internal/stream/encoding/encoding_test.go | 111 ++++++++++++++++++ internal/stream/encoding/int_test.go | 133 ++++++++++++++++++++++ 4 files changed, 286 insertions(+), 2 deletions(-) create mode 100644 internal/stream/encoding/bool_test.go create mode 100644 internal/stream/encoding/encoding_test.go create mode 100644 internal/stream/encoding/int_test.go diff --git a/internal/common/testutil/testutil.go b/internal/common/testutil/testutil.go index fded725..0d0f972 100644 --- a/internal/common/testutil/testutil.go +++ b/internal/common/testutil/testutil.go @@ -48,11 +48,21 @@ func Equal[T any](t *testing.T, actual, expected T) { func EqualSlice[T comparable](t *testing.T, actual, expected []T) { t.Helper() if len(actual) != len(expected) { - t.Fatalf("diffrent length. actual: %v, expected: %v", actual, expected) + switch any(actual).(type) { + case []byte: + t.Fatalf("diffrent length. actual: [% 02x], expected: [% 02x]", actual, expected) + default: + t.Fatalf("diffrent length. actual: %v, expected: %v", actual, expected) + } } for i := range actual { if !reflect.DeepEqual(actual[i], expected[i]) { - t.Fatalf("not equal. actual: %v, expected: %v", actual, expected) + switch any(actual).(type) { + case []byte: + t.Fatalf("not equal. actual: [% 02x], expected: [% 02x]", actual, expected) + default: + t.Fatalf("not equal. actual: %v, expected: %v", actual, expected) + } } } } diff --git a/internal/stream/encoding/bool_test.go b/internal/stream/encoding/bool_test.go new file mode 100644 index 0000000..5917828 --- /dev/null +++ b/internal/stream/encoding/bool_test.go @@ -0,0 +1,30 @@ +package encoding + +import ( + "testing" + + "github.com/shamaton/msgpack/v2/def" +) + +func Test_asBool(t *testing.T) { + method := func(e *encoder) func(bool) error { + return e.writeBool + } + testcases := AsXXXTestCases[bool]{ + { + Name: "True.error", + Value: true, + BufferSize: 1, + PreWriteSize: 1, + Method: method, + }, + { + Name: "True.ok", + Value: true, + Expected: []byte{def.True}, + BufferSize: 1, + Method: method, + }, + } + testcases.Run(t) +} diff --git a/internal/stream/encoding/encoding_test.go b/internal/stream/encoding/encoding_test.go new file mode 100644 index 0000000..0288605 --- /dev/null +++ b/internal/stream/encoding/encoding_test.go @@ -0,0 +1,111 @@ +package encoding + +import ( + "bytes" + "errors" + "io" + "reflect" + "testing" + + "github.com/shamaton/msgpack/v2/internal/common" + tu "github.com/shamaton/msgpack/v2/internal/common/testutil" +) + +const dummyByte = 0xc1 + +type TestWriter struct { + WrittenBytes []byte +} + +var _ io.Writer = (*TestWriter)(nil) + +var ErrTestWriter = errors.New("expected written error") + +func (w *TestWriter) Write(p []byte) (n int, err error) { + w.WrittenBytes = append(w.WrittenBytes, p...) + if bytes.Contains(p, []byte{dummyByte}) { + return 0, ErrTestWriter + } + return len(p), nil +} + +func NewTestWriter() *TestWriter { + return &TestWriter{} +} + +type AsXXXTestCase[T any] struct { + Name string + Value T + Expected []byte + Contains []byte + BufferSize int + PreWriteSize int + Method func(*encoder) func(T) error + MethodForFixed func(*encoder) func(reflect.Value) (bool, error) + MethodForStruct func(*encoder) func(reflect.Value) error +} + +type AsXXXTestCases[T any] []AsXXXTestCase[T] + +func (tcs AsXXXTestCases[T]) Run(t *testing.T) { + for _, tc := range tcs { + tc.Run(t) + } +} + +func (tc *AsXXXTestCase[T]) Run(t *testing.T) { + t.Helper() + + if tc.Method == nil && tc.MethodForFixed == nil && tc.MethodForStruct == nil { + t.Fatal("must set either Method or MethodForFixed or MethodForStruct") + } + + method := func(e *encoder) error { + if tc.Method != nil { + return tc.Method(e)(tc.Value) + } + if tc.MethodForFixed != nil { + _, err := tc.MethodForFixed(e)(reflect.ValueOf(tc.Value)) + return err + } + if tc.MethodForStruct != nil { + return tc.MethodForStruct(e)(reflect.ValueOf(tc.Value)) + } + panic("unreachable") + } + + t.Run(tc.Name, func(t *testing.T) { + w := NewTestWriter() + e := encoder{ + w: w, + buf: common.GetBuffer(), + Common: common.Common{}, + } + + if tc.BufferSize < tc.PreWriteSize { + t.Fatal("buffer size must be greater than pre write size") + } + + e.buf.Data = make([]byte, tc.BufferSize) + if tc.PreWriteSize > 0 { + for i := 0; i < tc.PreWriteSize; i++ { + _ = e.buf.Write(e.w, dummyByte) + } + } + + err := method(&e) + _ = e.buf.Flush(w) + common.PutBuffer(e.buf) + + if tc.PreWriteSize > 0 { + tu.IsError(t, err, ErrTestWriter) + if !bytes.Contains(w.WrittenBytes, tc.Contains) { + t.Fatalf("[% 02x] does not contain in [% 02x]", tc.Contains, w.WrittenBytes) + } + return + } + + tu.NoError(t, err) + tu.EqualSlice(t, w.WrittenBytes, tc.Expected) + }) +} diff --git a/internal/stream/encoding/int_test.go b/internal/stream/encoding/int_test.go new file mode 100644 index 0000000..a044fbc --- /dev/null +++ b/internal/stream/encoding/int_test.go @@ -0,0 +1,133 @@ +package encoding + +import ( + "math" + "testing" + + "github.com/shamaton/msgpack/v2/def" +) + +func Test_asInt(t *testing.T) { + method := func(e *encoder) func(int64) error { + return e.writeInt + } + testcases := AsXXXTestCases[int64]{ + { + Name: "Uint.error", + Value: math.MaxInt32, + BufferSize: 1, + PreWriteSize: 1, + Method: method, + }, + { + Name: "Uint.ok", + Value: math.MaxInt32, + Expected: []byte{def.Uint32, 0x7f, 0xff, 0xff, 0xff}, + BufferSize: 1, + Method: method, + }, + { + Name: "NegativeFix.error", + Value: -1, + BufferSize: 1, + PreWriteSize: 1, + Method: method, + }, + { + Name: "NegativeFix.ok", + Value: -1, + Expected: []byte{0xff}, + BufferSize: 1, + Method: method, + }, + { + Name: "Int8.error.def", + Value: math.MinInt8, + BufferSize: 1, + PreWriteSize: 1, + Method: method, + }, + { + Name: "Int8.error.value", + Value: math.MinInt8, + BufferSize: 2, + PreWriteSize: 1, + Contains: []byte{def.Int8}, + Method: method, + }, + { + Name: "Int8.ok", + Value: math.MinInt8, + Expected: []byte{def.Int8, 0x80}, + BufferSize: 1, + Method: method, + }, + { + Name: "Int16.error.def", + Value: math.MinInt16, + BufferSize: 1, + PreWriteSize: 1, + Method: method, + }, + { + Name: "Int16.error.value", + Value: math.MinInt16, + BufferSize: 3, + PreWriteSize: 1, + Contains: []byte{def.Int16}, + Method: method, + }, + { + Name: "Int16.ok", + Value: math.MinInt16, + Expected: []byte{def.Int16, 0x80, 0x00}, + BufferSize: 1, + Method: method, + }, + { + Name: "Int32.error.def", + Value: math.MinInt32, + BufferSize: 1, + PreWriteSize: 1, + Method: method, + }, + { + Name: "Int32.error.value", + Value: math.MinInt32, + BufferSize: 5, + PreWriteSize: 1, + Contains: []byte{def.Int32}, + Method: method, + }, + { + Name: "Int32.ok", + Value: math.MinInt32, + Expected: []byte{def.Int32, 0x80, 0x00, 0x00, 0x00}, + BufferSize: 1, + Method: method, + }, + { + Name: "Int64.error.def", + Value: math.MinInt64, + BufferSize: 1, + PreWriteSize: 1, + Method: method, + }, + { + Name: "Int64.error.value", + Value: math.MinInt64, + BufferSize: 9, + PreWriteSize: 1, + Contains: []byte{def.Int64}, + Method: method, + }, + { + Name: "Int64.ok", + Value: math.MinInt64, + Expected: []byte{def.Int64, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + BufferSize: 1, + Method: method, + }, + } + testcases.Run(t) +} From 1671bb8151a45b759dd112eea2e7249b2e1cbeaf Mon Sep 17 00:00:00 2001 From: shamaton Date: Thu, 22 Aug 2024 22:53:55 +0900 Subject: [PATCH 29/41] fix print format error --- internal/common/testutil/testutil.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/internal/common/testutil/testutil.go b/internal/common/testutil/testutil.go index 0d0f972..eb76a4b 100644 --- a/internal/common/testutil/testutil.go +++ b/internal/common/testutil/testutil.go @@ -48,18 +48,20 @@ func Equal[T any](t *testing.T, actual, expected T) { func EqualSlice[T comparable](t *testing.T, actual, expected []T) { t.Helper() if len(actual) != len(expected) { - switch any(actual).(type) { + switch a := any(actual).(type) { case []byte: - t.Fatalf("diffrent length. actual: [% 02x], expected: [% 02x]", actual, expected) + e := any(expected).([]byte) + t.Fatalf("diffrent length. actual: [% 02x], expected: [% 02x]", a, e) default: t.Fatalf("diffrent length. actual: %v, expected: %v", actual, expected) } } for i := range actual { if !reflect.DeepEqual(actual[i], expected[i]) { - switch any(actual).(type) { + switch a := any(actual).(type) { case []byte: - t.Fatalf("not equal. actual: [% 02x], expected: [% 02x]", actual, expected) + e := any(expected).([]byte) + t.Fatalf("not equal. actual: [% 02x], expected: [% 02x]", a, e) default: t.Fatalf("not equal. actual: %v, expected: %v", actual, expected) } From 85865ed0ec548a0242f3582d15f6a31a9b436e1a Mon Sep 17 00:00:00 2001 From: shamaton Date: Fri, 23 Aug 2024 00:56:28 +0900 Subject: [PATCH 30/41] add encoding map, slice tests --- internal/common/buffer.go | 2 +- internal/stream/encoding/map_test.go | 1234 ++++++++++++++++++++++++ internal/stream/encoding/slice_test.go | 399 ++++++++ 3 files changed, 1634 insertions(+), 1 deletion(-) create mode 100644 internal/stream/encoding/map_test.go create mode 100644 internal/stream/encoding/slice_test.go diff --git a/internal/common/buffer.go b/internal/common/buffer.go index c7c35c7..6ddec30 100644 --- a/internal/common/buffer.go +++ b/internal/common/buffer.go @@ -18,13 +18,13 @@ type Buffer struct { func (b *Buffer) Write(w io.Writer, vs ...byte) error { if len(b.Data) < b.offset+len(vs) { _, err := w.Write(b.Data[:b.offset]) + b.offset = 0 if err != nil { return err } if len(b.Data) < len(vs) { b.Data = append(b.Data, make([]byte, len(vs)-len(b.Data))...) } - b.offset = 0 } for i := range vs { b.Data[b.offset+i] = vs[i] diff --git a/internal/stream/encoding/map_test.go b/internal/stream/encoding/map_test.go new file mode 100644 index 0000000..4742262 --- /dev/null +++ b/internal/stream/encoding/map_test.go @@ -0,0 +1,1234 @@ +package encoding + +import ( + "math" + "reflect" + "testing" + + "github.com/shamaton/msgpack/v2/def" +) + +func Test_writeMapLength(t *testing.T) { + method := func(e *encoder) func(int) error { + return e.writeMapLength + } + testcases := AsXXXTestCases[int]{ + { + Name: "FixMap.error", + Value: 5, + BufferSize: 1, + PreWriteSize: 1, + Method: method, + }, + { + Name: "FixMap.ok", + Value: 5, + Expected: []byte{def.FixMap + 5}, + BufferSize: 1, + Method: method, + }, + { + Name: "Map16.error.def", + Value: 32, + BufferSize: 1, + PreWriteSize: 1, + Method: method, + }, + { + Name: "Map16.error.value", + Value: 32, + BufferSize: 3, + PreWriteSize: 1, + Contains: []byte{def.Map16}, + Method: method, + }, + { + Name: "Map16.ok", + Value: 32, + Expected: []byte{def.Map16, 0x00, 0x20}, + BufferSize: 1, + Method: method, + }, + { + Name: "Map32.error.def", + Value: 65536, + BufferSize: 1, + PreWriteSize: 1, + Method: method, + }, + { + Name: "Map32.error.value", + Value: 65536, + BufferSize: 3, + PreWriteSize: 1, + Contains: []byte{def.Map32}, + Method: method, + }, + { + Name: "Map32.ok", + Value: 65536, + Expected: []byte{def.Map32, 0x00, 0x01, 0x00, 0x00}, + BufferSize: 1, + Method: method, + }, + } + testcases.Run(t) +} + +func Test_writeFixedMap(t *testing.T) { + method := func(e *encoder) func(reflect.Value) (bool, error) { + return e.writeFixedMap + } + + t.Run("map[string]int", func(t *testing.T) { + value := map[string]int{"a": -1} + testcases := AsXXXTestCases[map[string]int]{ + { + Name: "error.key", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 3, + PreWriteSize: 1, + Contains: []byte{def.FixStr + 1, 0x61}, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{def.FixStr + 1, 0x61, 0xff}, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("map[string]uint", func(t *testing.T) { + value := map[string]uint{"a": 1} + testcases := AsXXXTestCases[map[string]uint]{ + { + Name: "error.key", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 3, + PreWriteSize: 1, + Contains: []byte{def.FixStr + 1, 0x61}, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{def.FixStr + 1, 0x61, 0x01}, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("map[string]float32", func(t *testing.T) { + value := map[string]float32{"a": 1} + testcases := AsXXXTestCases[map[string]float32]{ + { + Name: "error.key", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 3, + PreWriteSize: 1, + Contains: []byte{def.FixStr + 1, 0x61}, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{def.FixStr + 1, 0x61, def.Float32, 0x3f, 0x80, 0x00, 0x00}, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("map[string]float64", func(t *testing.T) { + value := map[string]float64{"a": 1} + testcases := AsXXXTestCases[map[string]float64]{ + { + Name: "error.key", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 3, + PreWriteSize: 1, + Contains: []byte{def.FixStr + 1, 0x61}, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.FixStr + 1, 0x61, + def.Float64, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("map[string]bool", func(t *testing.T) { + value := map[string]bool{"a": true} + testcases := AsXXXTestCases[map[string]bool]{ + { + Name: "error.key", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 3, + PreWriteSize: 1, + Contains: []byte{def.FixStr + 1, 0x61}, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{def.FixStr + 1, 0x61, def.True}, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("map[string]string", func(t *testing.T) { + value := map[string]string{"a": "b"} + testcases := AsXXXTestCases[map[string]string]{ + { + Name: "error.key", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 3, + PreWriteSize: 1, + Contains: []byte{def.FixStr + 1, 0x61}, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{def.FixStr + 1, 0x61, def.FixStr + 1, 0x62}, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("map[string]int8", func(t *testing.T) { + value := map[string]int8{"a": math.MinInt8} + testcases := AsXXXTestCases[map[string]int8]{ + { + Name: "error.key", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 3, + PreWriteSize: 1, + Contains: []byte{def.FixStr + 1, 0x61}, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.FixStr + 1, 0x61, + def.Int8, 0x80, + }, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("map[string]int16", func(t *testing.T) { + value := map[string]int16{"a": math.MinInt16} + testcases := AsXXXTestCases[map[string]int16]{ + { + Name: "error.key", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 3, + PreWriteSize: 1, + Contains: []byte{def.FixStr + 1, 0x61}, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.FixStr + 1, 0x61, + def.Int16, 0x80, 0x00, + }, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("map[string]int32", func(t *testing.T) { + value := map[string]int32{"a": math.MinInt32} + testcases := AsXXXTestCases[map[string]int32]{ + { + Name: "error.key", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 3, + PreWriteSize: 1, + Contains: []byte{def.FixStr + 1, 0x61}, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.FixStr + 1, 0x61, + def.Int32, 0x80, 0x00, 0x00, 0x00, + }, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("map[string]int64", func(t *testing.T) { + value := map[string]int64{"a": math.MinInt64} + testcases := AsXXXTestCases[map[string]int64]{ + { + Name: "error.key", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 3, + PreWriteSize: 1, + Contains: []byte{def.FixStr + 1, 0x61}, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.FixStr + 1, 0x61, + def.Int64, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("map[string]uint8", func(t *testing.T) { + value := map[string]uint8{"a": math.MaxUint8} + testcases := AsXXXTestCases[map[string]uint8]{ + { + Name: "error.key", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 3, + PreWriteSize: 1, + Contains: []byte{def.FixStr + 1, 0x61}, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.FixStr + 1, 0x61, + def.Uint8, 0xff, + }, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("map[string]uint16", func(t *testing.T) { + value := map[string]uint16{"a": math.MaxUint16} + testcases := AsXXXTestCases[map[string]uint16]{ + { + Name: "error.key", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 3, + PreWriteSize: 1, + Contains: []byte{def.FixStr + 1, 0x61}, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.FixStr + 1, 0x61, + def.Uint16, 0xff, 0xff, + }, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("map[string]uint32", func(t *testing.T) { + value := map[string]uint32{"a": math.MaxUint32} + testcases := AsXXXTestCases[map[string]uint32]{ + { + Name: "error.key", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 3, + PreWriteSize: 1, + Contains: []byte{def.FixStr + 1, 0x61}, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.FixStr + 1, 0x61, + def.Uint32, 0xff, 0xff, 0xff, 0xff, + }, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("map[string]uint64", func(t *testing.T) { + value := map[string]uint64{"a": math.MaxUint64} + testcases := AsXXXTestCases[map[string]uint64]{ + { + Name: "error.key", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 3, + PreWriteSize: 1, + Contains: []byte{def.FixStr + 1, 0x61}, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.FixStr + 1, 0x61, + def.Uint64, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("map[int]string", func(t *testing.T) { + value := map[int]string{math.MinInt8: "a"} + testcases := AsXXXTestCases[map[int]string]{ + { + Name: "error.key", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 3, + PreWriteSize: 1, + Contains: []byte{def.Int8, 0x80}, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{def.Int8, 0x80, def.FixStr + 1, 0x61}, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("map[int]bool", func(t *testing.T) { + value := map[int]bool{math.MinInt8: true} + testcases := AsXXXTestCases[map[int]bool]{ + { + Name: "error.key", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 3, + PreWriteSize: 1, + Contains: []byte{def.Int8, 0x80}, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{def.Int8, 0x80, def.True}, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("map[uint]string", func(t *testing.T) { + value := map[uint]string{math.MaxUint8: "a"} + testcases := AsXXXTestCases[map[uint]string]{ + { + Name: "error.key", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 3, + PreWriteSize: 1, + Contains: []byte{def.Uint8, 0xff}, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{def.Uint8, 0xff, def.FixStr + 1, 0x61}, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("map[uint]bool", func(t *testing.T) { + value := map[uint]bool{math.MaxUint8: true} + testcases := AsXXXTestCases[map[uint]bool]{ + { + Name: "error.key", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 3, + PreWriteSize: 1, + Contains: []byte{def.Uint8, 0xff}, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{def.Uint8, 0xff, def.True}, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("map[float32]string", func(t *testing.T) { + value := map[float32]string{1: "a"} + testcases := AsXXXTestCases[map[float32]string]{ + { + Name: "error.key", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 6, + PreWriteSize: 1, + Contains: []byte{def.Float32, 0x3f, 0x80, 0x00, 0x00}, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.Float32, 0x3f, 0x80, 0x00, 0x00, + def.FixStr + 1, 0x61, + }, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("map[float32]bool", func(t *testing.T) { + value := map[float32]bool{1: true} + testcases := AsXXXTestCases[map[float32]bool]{ + { + Name: "error.key", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 6, + PreWriteSize: 1, + Contains: []byte{def.Float32, 0x3f, 0x80, 0x00, 0x00}, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.Float32, 0x3f, 0x80, 0x00, 0x00, + def.True, + }, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("map[float64]string", func(t *testing.T) { + value := map[float64]string{1: "a"} + testcases := AsXXXTestCases[map[float64]string]{ + { + Name: "error.key", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 10, + PreWriteSize: 1, + Contains: []byte{def.Float64, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.Float64, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + def.FixStr + 1, 0x61, + }, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("map[float64]bool", func(t *testing.T) { + value := map[float64]bool{1: true} + testcases := AsXXXTestCases[map[float64]bool]{ + { + Name: "error.key", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 10, + PreWriteSize: 1, + Contains: []byte{def.Float64, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.Float64, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + def.True, + }, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("map[int8]string", func(t *testing.T) { + value := map[int8]string{math.MinInt8: "a"} + testcases := AsXXXTestCases[map[int8]string]{ + { + Name: "error.key", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 3, + PreWriteSize: 1, + Contains: []byte{def.Int8, 0x80}, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.Int8, 0x80, + def.FixStr + 1, 0x61, + }, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("map[int8]bool", func(t *testing.T) { + value := map[int8]bool{math.MinInt8: true} + testcases := AsXXXTestCases[map[int8]bool]{ + { + Name: "error.key", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 3, + PreWriteSize: 1, + Contains: []byte{def.Int8, 0x80}, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.Int8, 0x80, + def.True, + }, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("map[int16]string", func(t *testing.T) { + value := map[int16]string{math.MinInt16: "a"} + testcases := AsXXXTestCases[map[int16]string]{ + { + Name: "error.key", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 4, + PreWriteSize: 1, + Contains: []byte{def.Int16, 0x80, 0x00}, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.Int16, 0x80, 0x00, + def.FixStr + 1, 0x61, + }, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("map[int16]bool", func(t *testing.T) { + value := map[int16]bool{math.MinInt16: true} + testcases := AsXXXTestCases[map[int16]bool]{ + { + Name: "error.key", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 4, + PreWriteSize: 1, + Contains: []byte{def.Int16, 0x80, 0x00}, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.Int16, 0x80, 0x00, + def.True, + }, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("map[int32]string", func(t *testing.T) { + value := map[int32]string{math.MinInt32: "a"} + testcases := AsXXXTestCases[map[int32]string]{ + { + Name: "error.key", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 6, + PreWriteSize: 1, + Contains: []byte{def.Int32, 0x80, 0x00, 0x00, 0x00}, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.Int32, 0x80, 0x00, 0x00, 0x00, + def.FixStr + 1, 0x61, + }, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("map[int32]bool", func(t *testing.T) { + value := map[int32]bool{math.MinInt32: true} + testcases := AsXXXTestCases[map[int32]bool]{ + { + Name: "error.key", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 6, + PreWriteSize: 1, + Contains: []byte{def.Int32, 0x80, 0x00, 0x00, 0x00}, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.Int32, 0x80, 0x00, 0x00, 0x00, + def.True, + }, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("map[int64]string", func(t *testing.T) { + value := map[int64]string{math.MinInt64: "a"} + testcases := AsXXXTestCases[map[int64]string]{ + { + Name: "error.key", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 10, + PreWriteSize: 1, + Contains: []byte{def.Int64, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.Int64, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + def.FixStr + 1, 0x61, + }, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("map[int64]bool", func(t *testing.T) { + value := map[int64]bool{math.MinInt64: true} + testcases := AsXXXTestCases[map[int64]bool]{ + { + Name: "error.key", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 10, + PreWriteSize: 1, + Contains: []byte{def.Int64, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.Int64, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + def.True, + }, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + + t.Run("map[uint8]string", func(t *testing.T) { + value := map[uint8]string{math.MaxUint8: "a"} + testcases := AsXXXTestCases[map[uint8]string]{ + { + Name: "error.key", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 3, + PreWriteSize: 1, + Contains: []byte{def.Uint8, 0xff}, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.Uint8, 0xff, + def.FixStr + 1, 0x61, + }, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("map[uint8]bool", func(t *testing.T) { + value := map[uint8]bool{math.MaxUint8: true} + testcases := AsXXXTestCases[map[uint8]bool]{ + { + Name: "error.key", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 3, + PreWriteSize: 1, + Contains: []byte{def.Uint8, 0xff}, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.Uint8, 0xff, + def.True, + }, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("map[uint16]string", func(t *testing.T) { + value := map[uint16]string{math.MaxUint16: "a"} + testcases := AsXXXTestCases[map[uint16]string]{ + { + Name: "error.key", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 4, + PreWriteSize: 1, + Contains: []byte{def.Uint16, 0xff, 0xff}, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.Uint16, 0xff, 0xff, + def.FixStr + 1, 0x61, + }, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("map[uint16]bool", func(t *testing.T) { + value := map[uint16]bool{math.MaxUint16: true} + testcases := AsXXXTestCases[map[uint16]bool]{ + { + Name: "error.key", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 4, + PreWriteSize: 1, + Contains: []byte{def.Uint16, 0xff, 0xff}, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.Uint16, 0xff, 0xff, + def.True, + }, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("map[uint32]string", func(t *testing.T) { + value := map[uint32]string{math.MaxUint32: "a"} + testcases := AsXXXTestCases[map[uint32]string]{ + { + Name: "error.key", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 6, + PreWriteSize: 1, + Contains: []byte{def.Uint32, 0xff, 0xff, 0xff, 0xff}, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.Uint32, 0xff, 0xff, 0xff, 0xff, + def.FixStr + 1, 0x61, + }, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("map[uint32]bool", func(t *testing.T) { + value := map[uint32]bool{math.MaxUint32: true} + testcases := AsXXXTestCases[map[uint32]bool]{ + { + Name: "error.key", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 6, + PreWriteSize: 1, + Contains: []byte{def.Uint32, 0xff, 0xff, 0xff, 0xff}, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.Uint32, 0xff, 0xff, 0xff, 0xff, + def.True, + }, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("map[uint64]string", func(t *testing.T) { + value := map[uint64]string{math.MaxUint64: "a"} + testcases := AsXXXTestCases[map[uint64]string]{ + { + Name: "error.key", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 10, + PreWriteSize: 1, + Contains: []byte{def.Uint64, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.Uint64, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + def.FixStr + 1, 0x61, + }, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("map[uint64]bool", func(t *testing.T) { + value := map[uint64]bool{math.MaxUint64: true} + testcases := AsXXXTestCases[map[uint64]bool]{ + { + Name: "error.key", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 10, + PreWriteSize: 1, + Contains: []byte{def.Uint64, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.Uint64, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + def.True, + }, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) +} diff --git a/internal/stream/encoding/slice_test.go b/internal/stream/encoding/slice_test.go new file mode 100644 index 0000000..378d8fc --- /dev/null +++ b/internal/stream/encoding/slice_test.go @@ -0,0 +1,399 @@ +package encoding + +import ( + "math" + "reflect" + "testing" + + "github.com/shamaton/msgpack/v2/def" +) + +func Test_writeSliceLength(t *testing.T) { + method := func(e *encoder) func(int) error { + return e.writeSliceLength + } + testcases := AsXXXTestCases[int]{ + { + Name: "FixArray.error", + Value: 5, + BufferSize: 1, + PreWriteSize: 1, + Method: method, + }, + { + Name: "FixArray.ok", + Value: 5, + Expected: []byte{def.FixArray + 5}, + BufferSize: 1, + Method: method, + }, + { + Name: "Array16.error.def", + Value: 32, + BufferSize: 1, + PreWriteSize: 1, + Method: method, + }, + { + Name: "Array16.error.value", + Value: 32, + BufferSize: 3, + PreWriteSize: 1, + Contains: []byte{def.Array16}, + Method: method, + }, + { + Name: "Array16.ok", + Value: 32, + Expected: []byte{def.Array16, 0x00, 0x20}, + BufferSize: 1, + Method: method, + }, + { + Name: "Array32.error.def", + Value: 65536, + BufferSize: 1, + PreWriteSize: 1, + Method: method, + }, + { + Name: "Array32.error.value", + Value: 65536, + BufferSize: 3, + PreWriteSize: 1, + Contains: []byte{def.Array32}, + Method: method, + }, + { + Name: "Array32.ok", + Value: 65536, + Expected: []byte{def.Array32, 0x00, 0x01, 0x00, 0x00}, + BufferSize: 1, + Method: method, + }, + } + testcases.Run(t) +} + +func Test_writeFixedSlice(t *testing.T) { + method := func(e *encoder) func(reflect.Value) (bool, error) { + return e.writeFixedSlice + } + + t.Run("[]int", func(t *testing.T) { + value := []int{-1, -2, -3} + testcases := AsXXXTestCases[[]int]{ + { + Name: "error", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{0xff, 0xfe, 0xfd}, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("[]uint", func(t *testing.T) { + value := []uint{1, 2, 3} + testcases := AsXXXTestCases[[]uint]{ + { + Name: "error", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{0x01, 0x02, 0x03}, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("[]string", func(t *testing.T) { + value := []string{"a", "b", "c"} + testcases := AsXXXTestCases[[]string]{ + { + Name: "error", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{0xa1, 0x61, 0xa1, 0x62, 0xa1, 0x63}, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("[]float32", func(t *testing.T) { + value := []float32{4, 5, 6} + testcases := AsXXXTestCases[[]float32]{ + { + Name: "error", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{def.Float32, 0x40, 0x80, 0x00, 0x00, def.Float32, 0x40, 0xa0, 0x00, 0x00, def.Float32, 0x40, 0xc0, 0x00, 0x00}, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("[]float64", func(t *testing.T) { + value := []float64{4, 5, 6} + testcases := AsXXXTestCases[[]float64]{ + { + Name: "error", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.Float64, 0x40, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + def.Float64, 0x40, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + def.Float64, 0x40, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("[]bool", func(t *testing.T) { + value := []bool{true, false, true} + testcases := AsXXXTestCases[[]bool]{ + { + Name: "error", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{def.True, def.False, def.True}, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("[]int8", func(t *testing.T) { + value := []int8{math.MinInt8, math.MinInt8 + 1, math.MinInt8 + 2} + testcases := AsXXXTestCases[[]int8]{ + { + Name: "error", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.Int8, 0x80, + def.Int8, 0x81, + def.Int8, 0x82, + }, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("[]int16", func(t *testing.T) { + value := []int16{math.MinInt16, math.MinInt16 + 1, math.MinInt16 + 2} + testcases := AsXXXTestCases[[]int16]{ + { + Name: "error", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.Int16, 0x80, 0x00, + def.Int16, 0x80, 0x01, + def.Int16, 0x80, 0x02, + }, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("[]int32", func(t *testing.T) { + value := []int32{math.MinInt32, math.MinInt32 + 1, math.MinInt32 + 2} + testcases := AsXXXTestCases[[]int32]{ + { + Name: "error", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.Int32, 0x80, 0x00, 0x00, 0x00, + def.Int32, 0x80, 0x00, 0x00, 0x01, + def.Int32, 0x80, 0x00, 0x00, 0x02, + }, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("[]int64", func(t *testing.T) { + value := []int64{math.MinInt64, math.MinInt64 + 1, math.MinInt64 + 2} + testcases := AsXXXTestCases[[]int64]{ + { + Name: "error", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.Int64, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + def.Int64, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + def.Int64, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + }, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("[]uint8", func(t *testing.T) { + value := []uint8{math.MaxUint8, math.MaxUint8 - 1, math.MaxUint8 - 2} + testcases := AsXXXTestCases[[]uint8]{ + { + Name: "error", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.Uint8, 0xff, + def.Uint8, 0xfe, + def.Uint8, 0xfd, + }, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("[]uint16", func(t *testing.T) { + value := []uint16{math.MaxUint16, math.MaxUint16 - 1, math.MaxUint16 - 2} + testcases := AsXXXTestCases[[]uint16]{ + { + Name: "error", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.Uint16, 0xff, 0xff, + def.Uint16, 0xff, 0xfe, + def.Uint16, 0xff, 0xfd, + }, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("[]uint32", func(t *testing.T) { + value := []uint32{math.MaxUint32, math.MaxUint32 - 1, math.MaxUint32 - 2} + testcases := AsXXXTestCases[[]uint32]{ + { + Name: "error", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.Uint32, 0xff, 0xff, 0xff, 0xff, + def.Uint32, 0xff, 0xff, 0xff, 0xfe, + def.Uint32, 0xff, 0xff, 0xff, 0xfd, + }, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) + t.Run("[]uint64", func(t *testing.T) { + value := []uint64{math.MaxUint64, math.MaxUint64 - 1, math.MaxUint64 - 2} + testcases := AsXXXTestCases[[]uint64]{ + { + Name: "error", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForFixed: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.Uint64, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + def.Uint64, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, + def.Uint64, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, + }, + BufferSize: 1, + MethodForFixed: method, + }, + } + testcases.Run(t) + }) +} From e6c3a73ec1472f96b8b256fed587515a42914860 Mon Sep 17 00:00:00 2001 From: shamaton Date: Fri, 23 Aug 2024 22:43:45 +0900 Subject: [PATCH 31/41] add encording primitive type tests --- internal/stream/encoding/byte_test.go | 82 ++++++++++++++++ internal/stream/encoding/complex_test.go | 117 ++++++++++++++++++++++ internal/stream/encoding/float_test.go | 77 +++++++++++++++ internal/stream/encoding/string_test.go | 119 +++++++++++++++++++++++ internal/stream/encoding/uint_test.go | 119 +++++++++++++++++++++++ 5 files changed, 514 insertions(+) create mode 100644 internal/stream/encoding/byte_test.go create mode 100644 internal/stream/encoding/complex_test.go create mode 100644 internal/stream/encoding/float_test.go create mode 100644 internal/stream/encoding/string_test.go create mode 100644 internal/stream/encoding/uint_test.go diff --git a/internal/stream/encoding/byte_test.go b/internal/stream/encoding/byte_test.go new file mode 100644 index 0000000..2300a6d --- /dev/null +++ b/internal/stream/encoding/byte_test.go @@ -0,0 +1,82 @@ +package encoding + +import ( + "testing" + + "github.com/shamaton/msgpack/v2/def" +) + +func Test_writeByteSliceLength(t *testing.T) { + method := func(e *encoder) func(int) error { + return e.writeByteSliceLength + } + testcases := AsXXXTestCases[int]{ + { + Name: "Bin8.error.def", + Value: 5, + BufferSize: 1, + PreWriteSize: 1, + Method: method, + }, + { + Name: "Bin8.error.value", + Value: 5, + BufferSize: 2, + PreWriteSize: 1, + Contains: []byte{def.Bin8}, + Method: method, + }, + { + Name: "Bin8.ok", + Value: 5, + Expected: []byte{def.Bin8, 0x05}, + BufferSize: 1, + Method: method, + }, + { + Name: "Bin16.error.def", + Value: 256, + BufferSize: 1, + PreWriteSize: 1, + Method: method, + }, + { + Name: "Bin16.error.value", + Value: 256, + BufferSize: 3, + PreWriteSize: 1, + Contains: []byte{def.Bin16}, + Method: method, + }, + { + Name: "Bin16.ok", + Value: 256, + Expected: []byte{def.Bin16, 0x01, 0x00}, + BufferSize: 1, + Method: method, + }, + { + Name: "Bin32.error.def", + Value: 65536, + BufferSize: 1, + PreWriteSize: 1, + Method: method, + }, + { + Name: "Bin32.error.value", + Value: 65536, + BufferSize: 3, + PreWriteSize: 1, + Contains: []byte{def.Bin32}, + Method: method, + }, + { + Name: "Bin32.ok", + Value: 65536, + Expected: []byte{def.Bin32, 0x00, 0x01, 0x00, 0x00}, + BufferSize: 1, + Method: method, + }, + } + testcases.Run(t) +} diff --git a/internal/stream/encoding/complex_test.go b/internal/stream/encoding/complex_test.go new file mode 100644 index 0000000..84cfe0b --- /dev/null +++ b/internal/stream/encoding/complex_test.go @@ -0,0 +1,117 @@ +package encoding + +import ( + "testing" + + "github.com/shamaton/msgpack/v2/def" +) + +func Test_writeComplex64(t *testing.T) { + method := func(e *encoder) func(complex64) error { + return e.writeComplex64 + } + v := complex64(complex(1, 2)) + testcases := AsXXXTestCases[complex64]{ + { + Name: "error.def", + Value: v, + BufferSize: 1, + PreWriteSize: 1, + Method: method, + }, + { + Name: "error.code", + Value: v, + BufferSize: 2, + PreWriteSize: 1, + Contains: []byte{def.Fixext8}, + Method: method, + }, + { + Name: "error.real", + Value: v, + BufferSize: 3, + PreWriteSize: 1, + Contains: []byte{def.Fixext8, byte(def.ComplexTypeCode())}, + Method: method, + }, + { + Name: "error.imag", + Value: v, + BufferSize: 7, + PreWriteSize: 1, + Contains: []byte{ + def.Fixext8, byte(def.ComplexTypeCode()), + 0x3f, 0x80, 0x00, 0x00, + }, + Method: method, + }, + { + Name: "ok", + Value: v, + Expected: []byte{ + def.Fixext8, byte(def.ComplexTypeCode()), + 0x3f, 0x80, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, + }, + BufferSize: 1, + Method: method, + }, + } + testcases.Run(t) +} + +func Test_writeComplex128(t *testing.T) { + method := func(e *encoder) func(complex128) error { + return e.writeComplex128 + } + v := complex128(complex(1, 2)) + testcases := AsXXXTestCases[complex128]{ + { + Name: "error.def", + Value: v, + BufferSize: 1, + PreWriteSize: 1, + Method: method, + }, + { + Name: "error.code", + Value: v, + BufferSize: 2, + PreWriteSize: 1, + Contains: []byte{def.Fixext16}, + Method: method, + }, + { + Name: "error.real", + Value: v, + BufferSize: 3, + PreWriteSize: 1, + Contains: []byte{def.Fixext16, byte(def.ComplexTypeCode())}, + Method: method, + }, + { + Name: "error.imag", + Value: v, + BufferSize: 11, + PreWriteSize: 1, + Contains: []byte{ + def.Fixext16, byte(def.ComplexTypeCode()), + 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + Method: method, + }, + { + Name: "ok", + Value: v, + Expected: []byte{ + def.Fixext16, byte(def.ComplexTypeCode()), + 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + BufferSize: 1, + Method: method, + }, + } + testcases.Run(t) +} diff --git a/internal/stream/encoding/float_test.go b/internal/stream/encoding/float_test.go new file mode 100644 index 0000000..dd969a6 --- /dev/null +++ b/internal/stream/encoding/float_test.go @@ -0,0 +1,77 @@ +package encoding + +import ( + "testing" + + "github.com/shamaton/msgpack/v2/def" +) + +func Test_writeFloat32(t *testing.T) { + method := func(e *encoder) func(float64) error { + return e.writeFloat32 + } + v := 1.23 + testcases := AsXXXTestCases[float64]{ + { + Name: "error.def", + Value: v, + BufferSize: 1, + PreWriteSize: 1, + Method: method, + }, + { + Name: "error.value", + Value: v, + BufferSize: 2, + PreWriteSize: 1, + Contains: []byte{def.Float32}, + Method: method, + }, + { + Name: "ok", + Value: v, + Expected: []byte{ + def.Float32, + 0x3f, 0x9d, 0x70, 0xa4, + }, + BufferSize: 1, + Method: method, + }, + } + testcases.Run(t) +} + +func Test_writeFloat64(t *testing.T) { + method := func(e *encoder) func(float64) error { + return e.writeFloat64 + } + v := 1.23 + testcases := AsXXXTestCases[float64]{ + { + Name: "error.def", + Value: v, + BufferSize: 1, + PreWriteSize: 1, + Method: method, + }, + { + Name: "error.value", + Value: v, + BufferSize: 2, + PreWriteSize: 1, + Contains: []byte{def.Float64}, + Method: method, + }, + { + Name: "ok", + Value: v, + Expected: []byte{ + def.Float64, + 0x3f, 0xf3, 0xae, 0x14, 0x7a, 0xe1, 0x47, 0xae, + }, + BufferSize: 1, + Method: method, + }, + } + testcases.Run(t) +} diff --git a/internal/stream/encoding/string_test.go b/internal/stream/encoding/string_test.go new file mode 100644 index 0000000..1ef24b2 --- /dev/null +++ b/internal/stream/encoding/string_test.go @@ -0,0 +1,119 @@ +package encoding + +import ( + "math" + "strings" + "testing" + + "github.com/shamaton/msgpack/v2/def" +) + +func Test_writeString(t *testing.T) { + method := func(e *encoder) func(string) error { + return e.writeString + } + str8 := strings.Repeat("a", math.MaxUint8) + str16 := strings.Repeat("a", math.MaxUint16) + str32 := strings.Repeat("a", math.MaxUint16+1) + testcases := AsXXXTestCases[string]{ + { + Name: "Str8.error.def", + Value: str8, + BufferSize: 1, + PreWriteSize: 1, + Method: method, + }, + { + Name: "Str8.error.length", + Value: str8, + BufferSize: 2, + PreWriteSize: 1, + Contains: []byte{def.Str8}, + Method: method, + }, + { + Name: "Str8.error.string", + Value: str8, + BufferSize: 3, + PreWriteSize: 1, + Contains: []byte{def.Str8, 0xff}, + Method: method, + }, + { + Name: "Str8.ok", + Value: str8, + Expected: append( + []byte{def.Str8, 0xff}, + []byte(str8)..., + ), + BufferSize: 1, + Method: method, + }, { + Name: "Str16.error.def", + Value: str16, + BufferSize: 1, + PreWriteSize: 1, + Method: method, + }, + { + Name: "Str16.error.length", + Value: str16, + BufferSize: 2, + PreWriteSize: 1, + Contains: []byte{def.Str16}, + Method: method, + }, + { + Name: "Str16.error.string", + Value: str16, + BufferSize: 4, + PreWriteSize: 1, + Contains: []byte{def.Str16, 0xff, 0xff}, + Method: method, + }, + { + Name: "Str16.ok", + Value: str16, + Expected: append( + []byte{def.Str16, 0xff, 0xff}, + []byte(str16)..., + ), + BufferSize: 1, + Method: method, + }, + { + Name: "Str32.error.def", + Value: str32, + BufferSize: 1, + PreWriteSize: 1, + Method: method, + }, + { + Name: "Str32.error.length", + Value: str32, + BufferSize: 2, + PreWriteSize: 1, + Contains: []byte{def.Str32}, + Method: method, + }, + { + Name: "Str32.error.string", + Value: str32, + BufferSize: 6, + PreWriteSize: 1, + Contains: []byte{def.Str32, 0x00, 0x01, 0x00, 0x00}, + Method: method, + }, + { + Name: "Str32.ok", + Value: str32, + Expected: append( + []byte{def.Str32, 0x00, 0x01, 0x00, 0x00}, + []byte(str32)..., + ), + BufferSize: 1, + Method: method, + }, + } + testcases.Run(t) +} diff --git a/internal/stream/encoding/uint_test.go b/internal/stream/encoding/uint_test.go new file mode 100644 index 0000000..03f605a --- /dev/null +++ b/internal/stream/encoding/uint_test.go @@ -0,0 +1,119 @@ +package encoding + +import ( + "math" + "testing" + + "github.com/shamaton/msgpack/v2/def" +) + +func Test_asUint(t *testing.T) { + method := func(e *encoder) func(uint64) error { + return e.writeUint + } + testcases := AsXXXTestCases[uint64]{ + { + Name: "PositiveFix.error", + Value: math.MaxInt8, + BufferSize: 1, + PreWriteSize: 1, + Method: method, + }, + { + Name: "PositiveFix.ok", + Value: math.MaxInt8, + Expected: []byte{0x7f}, + BufferSize: 1, + Method: method, + }, + { + Name: "Uint8.error.def", + Value: math.MaxUint8, + BufferSize: 1, + PreWriteSize: 1, + Method: method, + }, + { + Name: "Uint8.error.value", + Value: math.MaxUint8, + BufferSize: 2, + PreWriteSize: 1, + Contains: []byte{def.Uint8}, + Method: method, + }, + { + Name: "Uint8.ok", + Value: math.MaxUint8, + Expected: []byte{def.Uint8, 0xff}, + BufferSize: 1, + Method: method, + }, + { + Name: "Uint16.error.def", + Value: math.MaxUint16, + BufferSize: 1, + PreWriteSize: 1, + Method: method, + }, + { + Name: "Uint16.error.value", + Value: math.MaxUint16, + BufferSize: 3, + PreWriteSize: 1, + Contains: []byte{def.Uint16}, + Method: method, + }, + { + Name: "Uint16.ok", + Value: math.MaxUint16, + Expected: []byte{def.Uint16, 0xff, 0xff}, + BufferSize: 1, + Method: method, + }, + { + Name: "Uint32.error.def", + Value: math.MaxUint32, + BufferSize: 1, + PreWriteSize: 1, + Method: method, + }, + { + Name: "Uint32.error.value", + Value: math.MaxUint32, + BufferSize: 5, + PreWriteSize: 1, + Contains: []byte{def.Uint32}, + Method: method, + }, + { + Name: "Uint32.ok", + Value: math.MaxUint32, + Expected: []byte{def.Uint32, 0xff, 0xff, 0xff, 0xff}, + BufferSize: 1, + Method: method, + }, + { + Name: "Uint64.error.def", + Value: math.MaxUint64, + BufferSize: 1, + PreWriteSize: 1, + Method: method, + }, + { + Name: "Uint64.error.value", + Value: math.MaxUint64, + BufferSize: 9, + PreWriteSize: 1, + Contains: []byte{def.Uint64}, + Method: method, + }, + { + Name: "Uint64.ok", + Value: math.MaxUint64, + Expected: []byte{def.Uint64, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + BufferSize: 1, + Method: method, + }, + } + testcases.Run(t) +} From bf52e7801061dbfcc26bdf82fce94957adff9778 Mon Sep 17 00:00:00 2001 From: shamaton Date: Sat, 24 Aug 2024 00:35:55 +0900 Subject: [PATCH 32/41] add encoding main, ext tests --- def/error.go | 1 + internal/decoding/decoding.go | 2 +- internal/encoding/encoding.go | 2 +- internal/stream/decoding/decoding.go | 2 +- internal/stream/encoding/encoding.go | 3 +- internal/stream/encoding/encoding_test.go | 786 ++++++++++++++++++++++ internal/stream/encoding/ext_test.go | 22 + msgpack_test.go | 12 +- 8 files changed, 820 insertions(+), 10 deletions(-) create mode 100644 internal/stream/encoding/ext_test.go diff --git a/def/error.go b/def/error.go index 4e653d0..d57b404 100644 --- a/def/error.go +++ b/def/error.go @@ -14,4 +14,5 @@ var ( ErrTooShortBytes = errors.New("too short bytes") ErrLackDataLengthToSlice = errors.New("data length lacks to create slice") ErrLackDataLengthToMap = errors.New("data length lacks to create map") + ErrUnsupported = errors.New("unsupported") ) diff --git a/internal/decoding/decoding.go b/internal/decoding/decoding.go index 3c03775..1ceba9b 100644 --- a/internal/decoding/decoding.go +++ b/internal/decoding/decoding.go @@ -333,7 +333,7 @@ func (d *decoder) decode(rv reflect.Value, offset int) (int, error) { } default: - return 0, fmt.Errorf("type(%v) is unsupported", rv.Kind()) + return 0, fmt.Errorf("%v is %w type", rv.Kind(), def.ErrUnsupported) } return offset, nil } diff --git a/internal/encoding/encoding.go b/internal/encoding/encoding.go index ab8f865..51e1618 100644 --- a/internal/encoding/encoding.go +++ b/internal/encoding/encoding.go @@ -262,7 +262,7 @@ func (e *encoder) calcSize(rv reflect.Value) (int, error) { // do nothing (return nil) default: - return 0, fmt.Errorf("type(%v) is unsupported", rv.Kind()) + return 0, fmt.Errorf("%v is %w type", rv.Kind(), def.ErrUnsupported) } return ret, nil diff --git a/internal/stream/decoding/decoding.go b/internal/stream/decoding/decoding.go index 4e82e36..cb5288e 100644 --- a/internal/stream/decoding/decoding.go +++ b/internal/stream/decoding/decoding.go @@ -314,7 +314,7 @@ func (d *decoder) decodeWithCode(code byte, rv reflect.Value) error { } default: - return fmt.Errorf("type(%v) is unsupported", rv.Kind()) + return fmt.Errorf("%v is %w type", rv.Kind(), def.ErrUnsupported) } return nil } diff --git a/internal/stream/encoding/encoding.go b/internal/stream/encoding/encoding.go index 7123cdc..2613f37 100644 --- a/internal/stream/encoding/encoding.go +++ b/internal/stream/encoding/encoding.go @@ -5,6 +5,7 @@ import ( "io" "reflect" + "github.com/shamaton/msgpack/v2/def" "github.com/shamaton/msgpack/v2/internal/common" ) @@ -188,7 +189,7 @@ func (e *encoder) create(rv reflect.Value) error { case reflect.Invalid: return e.writeNil() default: - return fmt.Errorf("type(%v) is unsupported", rv.Kind()) + return fmt.Errorf("%v is %w type", rv.Kind(), def.ErrUnsupported) } return nil } diff --git a/internal/stream/encoding/encoding_test.go b/internal/stream/encoding/encoding_test.go index 0288605..dd983ca 100644 --- a/internal/stream/encoding/encoding_test.go +++ b/internal/stream/encoding/encoding_test.go @@ -7,6 +7,7 @@ import ( "reflect" "testing" + "github.com/shamaton/msgpack/v2/def" "github.com/shamaton/msgpack/v2/internal/common" tu "github.com/shamaton/msgpack/v2/internal/common/testutil" ) @@ -40,6 +41,7 @@ type AsXXXTestCase[T any] struct { Contains []byte BufferSize int PreWriteSize int + Error error Method func(*encoder) func(T) error MethodForFixed func(*encoder) func(reflect.Value) (bool, error) MethodForStruct func(*encoder) func(reflect.Value) error @@ -104,8 +106,792 @@ func (tc *AsXXXTestCase[T]) Run(t *testing.T) { } return } + if tc.Error != nil { + tu.IsError(t, err, tc.Error) + return + } tu.NoError(t, err) tu.EqualSlice(t, w.WrittenBytes, tc.Expected) }) } + +func TestEncode(t *testing.T) { + v := 1 + vv := &v + + w := NewTestWriter() + err := Encode(w, &vv, false) + tu.NoError(t, err) + + tu.EqualSlice(t, w.WrittenBytes, []byte{def.PositiveFixIntMin + 1}) +} + +func Test_create(t *testing.T) { + method := func(e *encoder) func(reflect.Value) error { + return e.create + } + + t.Run("uint8", func(t *testing.T) { + value := uint8(1) + testcases := AsXXXTestCases[uint8]{ + { + Name: "error", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForStruct: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{def.PositiveFixIntMin + 1}, + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + t.Run("uint16", func(t *testing.T) { + value := uint16(1) + testcases := AsXXXTestCases[uint16]{ + { + Name: "error", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForStruct: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{def.PositiveFixIntMin + 1}, + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + t.Run("uint32", func(t *testing.T) { + value := uint32(1) + testcases := AsXXXTestCases[uint32]{ + { + Name: "error", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForStruct: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{def.PositiveFixIntMin + 1}, + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + t.Run("uint64", func(t *testing.T) { + value := uint64(1) + testcases := AsXXXTestCases[uint64]{ + { + Name: "error", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForStruct: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{def.PositiveFixIntMin + 1}, + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + t.Run("uint", func(t *testing.T) { + value := uint(1) + testcases := AsXXXTestCases[uint]{ + { + Name: "error", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForStruct: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{def.PositiveFixIntMin + 1}, + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + t.Run("int8", func(t *testing.T) { + value := int8(-1) + testcases := AsXXXTestCases[int8]{ + { + Name: "error", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForStruct: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{0xff}, + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + t.Run("int16", func(t *testing.T) { + value := int16(-1) + testcases := AsXXXTestCases[int16]{ + { + Name: "error", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForStruct: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{0xff}, + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + t.Run("int32", func(t *testing.T) { + value := int32(-1) + testcases := AsXXXTestCases[int32]{ + { + Name: "error", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForStruct: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{0xff}, + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + t.Run("int64", func(t *testing.T) { + value := int64(-1) + testcases := AsXXXTestCases[int64]{ + { + Name: "error", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForStruct: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{0xff}, + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + t.Run("int", func(t *testing.T) { + value := int(-1) + testcases := AsXXXTestCases[int]{ + { + Name: "error", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForStruct: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{0xff}, + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + t.Run("float32", func(t *testing.T) { + value := float32(1) + testcases := AsXXXTestCases[float32]{ + { + Name: "error", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForStruct: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{def.Float32, 0x3f, 0x80, 0x00, 0x00}, + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + t.Run("float64", func(t *testing.T) { + value := float64(1) + testcases := AsXXXTestCases[float64]{ + { + Name: "error", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForStruct: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{def.Float64, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + t.Run("bool", func(t *testing.T) { + value := true + testcases := AsXXXTestCases[bool]{ + { + Name: "error", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForStruct: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{def.True}, + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + t.Run("string", func(t *testing.T) { + value := "a" + testcases := AsXXXTestCases[string]{ + { + Name: "error", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForStruct: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{def.FixStr + 1, 0x61}, + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + t.Run("complex64", func(t *testing.T) { + value := complex64(complex(1, 2)) + testcases := AsXXXTestCases[complex64]{ + { + Name: "error", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForStruct: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.Fixext8, byte(def.ComplexTypeCode()), + 0x3f, 0x80, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, + }, + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + t.Run("complex128", func(t *testing.T) { + value := complex128(complex(1, 2)) + testcases := AsXXXTestCases[complex128]{ + { + Name: "error", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForStruct: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{ + def.Fixext16, byte(def.ComplexTypeCode()), + 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + + type st struct { + A int + } + + t.Run("slice", func(t *testing.T) { + t.Run("nil", func(t *testing.T) { + var value []int + testcases := AsXXXTestCases[[]int]{ + { + Name: "ok", + Value: value, + Expected: []byte{def.Nil}, + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + t.Run("bin", func(t *testing.T) { + value := []byte{1, 2, 3} + testcases := AsXXXTestCases[[]byte]{ + { + Name: "error.length", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForStruct: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 3, + PreWriteSize: 1, + Contains: []byte{def.Bin8, 0x03}, + MethodForStruct: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{def.Bin8, 0x03, 0x01, 0x02, 0x03}, + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + t.Run("fixed", func(t *testing.T) { + value := []int{1, 2, 3} + testcases := AsXXXTestCases[[]int]{ + { + Name: "error.length", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForStruct: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 2, + PreWriteSize: 1, + Contains: []byte{def.FixArray + 3}, + MethodForStruct: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{def.FixArray + 3, 0x01, 0x02, 0x03}, + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + t.Run("slice_slice", func(t *testing.T) { + value := [][]int{{1}} + testcases := AsXXXTestCases[[][]int]{ + { + Name: "error.length", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForStruct: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 2, + PreWriteSize: 1, + Contains: []byte{def.FixArray + 1}, + MethodForStruct: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{def.FixArray + 1, def.FixArray + 1, 0x01}, + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + t.Run("struct", func(t *testing.T) { + value := []st{{1}} + testcases := AsXXXTestCases[[]st]{ + { + Name: "error.length", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForStruct: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 2, + PreWriteSize: 1, + Contains: []byte{def.FixArray + 1}, + MethodForStruct: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{def.FixArray + 1, def.FixMap + 1, def.FixStr + 1, 0x41, 0x01}, + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + }) + + t.Run("array", func(t *testing.T) { + t.Run("bin", func(t *testing.T) { + value := [3]byte{1, 2, 3} + testcases := AsXXXTestCases[[3]byte]{ + { + Name: "error.length", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForStruct: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 3, + PreWriteSize: 1, + Contains: []byte{def.Bin8, 0x03}, + MethodForStruct: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{def.Bin8, 0x03, 0x01, 0x02, 0x03}, + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + t.Run("fixed", func(t *testing.T) { + value := [3]int{1, 2, 3} + testcases := AsXXXTestCases[[3]int]{ + { + Name: "error.length", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForStruct: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 2, + PreWriteSize: 1, + Contains: []byte{def.FixArray + 3}, + MethodForStruct: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{def.FixArray + 3, 0x01, 0x02, 0x03}, + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + t.Run("slice_slice", func(t *testing.T) { + value := [1][]int{{1}} + testcases := AsXXXTestCases[[1][]int]{ + { + Name: "error.length", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForStruct: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 2, + PreWriteSize: 1, + Contains: []byte{def.FixArray + 1}, + MethodForStruct: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{def.FixArray + 1, def.FixArray + 1, 0x01}, + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + t.Run("struct", func(t *testing.T) { + value := [1]st{{1}} + testcases := AsXXXTestCases[[1]st]{ + { + Name: "error.length", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForStruct: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 2, + PreWriteSize: 1, + Contains: []byte{def.FixArray + 1}, + MethodForStruct: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{def.FixArray + 1, def.FixMap + 1, def.FixStr + 1, 0x41, 0x01}, + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + }) + + t.Run("map", func(t *testing.T) { + t.Run("nil", func(t *testing.T) { + var value map[string]int + testcases := AsXXXTestCases[map[string]int]{ + { + Name: "ok", + Value: value, + Expected: []byte{def.Nil}, + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + t.Run("fixed", func(t *testing.T) { + value := map[string]int{"a": 1} + testcases := AsXXXTestCases[map[string]int]{ + { + Name: "error.length", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForStruct: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 2, + PreWriteSize: 1, + Contains: []byte{def.FixMap + 1}, + MethodForStruct: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{def.FixMap + 1, def.FixStr + 1, 'a', 1}, + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + t.Run("struct", func(t *testing.T) { + value := map[string]st{"a": {1}} + testcases := AsXXXTestCases[map[string]st]{ + { + Name: "error.length", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForStruct: method, + }, + { + Name: "error.key", + Value: value, + BufferSize: 2, + PreWriteSize: 1, + Contains: []byte{def.FixMap + 1}, + MethodForStruct: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 4, + PreWriteSize: 1, + Contains: []byte{def.FixMap + 1, def.FixStr + 1, 'a'}, + MethodForStruct: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{def.FixMap + 1, def.FixStr + 1, 'a', def.FixMap + 1, def.FixStr + 1, 'A', 1}, + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + }) + + t.Run("struct", func(t *testing.T) { + value := st{1} + testcases := AsXXXTestCases[st]{ + { + Name: "error", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForStruct: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{def.FixMap + 1, def.FixStr + 1, 'A', 1}, + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + t.Run("pointer", func(t *testing.T) { + var v *int + vv := &v + value := &vv + t.Run("nil", func(t *testing.T) { + testcases := AsXXXTestCases[***int]{ + { + Name: "nil", + Value: value, + Expected: []byte{def.Nil}, + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + u := 1 + v = &u + vv = &v + value = &vv + t.Run("int", func(t *testing.T) { + testcases := AsXXXTestCases[***int]{ + { + Name: "error", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForStruct: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{1}, + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + }) + t.Run("any", func(t *testing.T) { + value := any(1) + testcases := AsXXXTestCases[any]{ + { + Name: "error", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForStruct: method, + }, + { + Name: "ok", + Value: value, + Expected: []byte{1}, + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + t.Run("invalid", func(t *testing.T) { + var value error + testcases := AsXXXTestCases[error]{ + { + Name: "ok", + Value: value, + Expected: []byte{def.Nil}, + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + t.Run("invalid", func(t *testing.T) { + var value func() + testcases := AsXXXTestCases[func()]{ + { + Name: "ok", + Value: value, + BufferSize: 1, + Error: def.ErrUnsupported, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) +} diff --git a/internal/stream/encoding/ext_test.go b/internal/stream/encoding/ext_test.go new file mode 100644 index 0000000..c6c4a07 --- /dev/null +++ b/internal/stream/encoding/ext_test.go @@ -0,0 +1,22 @@ +package encoding + +import ( + "testing" + + tu "github.com/shamaton/msgpack/v2/internal/common/testutil" + "github.com/shamaton/msgpack/v2/time" +) + +func Test_AddExtEncoder(t *testing.T) { + t.Run("ignore", func(t *testing.T) { + AddExtEncoder(time.StreamEncoder) + tu.Equal(t, len(extCoders), 1) + }) +} + +func Test_RemoveExtEncoder(t *testing.T) { + t.Run("ignore", func(t *testing.T) { + RemoveExtEncoder(time.StreamEncoder) + tu.Equal(t, len(extCoders), 1) + }) +} diff --git a/msgpack_test.go b/msgpack_test.go index f2ea867..b409b53 100644 --- a/msgpack_test.go +++ b/msgpack_test.go @@ -1510,14 +1510,14 @@ func TestUnsupported(t *testing.T) { t.Run(m.name, func(t *testing.T) { var v uintptr _, err := m.m(v) - ErrorContains(t, err, "type(uintptr) is unsupported") + ErrorContains(t, err, "uintptr is unsupported type") }) } for _, u := range unmarshallers { t.Run(u.name, func(t *testing.T) { var r uintptr err := u.u(b, &r) - ErrorContains(t, err, "type(uintptr) is unsupported") + ErrorContains(t, err, "uintptr is unsupported type") }) } }) @@ -1526,14 +1526,14 @@ func TestUnsupported(t *testing.T) { t.Run(m.name, func(t *testing.T) { var v chan string _, err := m.m(v) - ErrorContains(t, err, "type(chan) is unsupported") + ErrorContains(t, err, "chan is unsupported type") }) } for _, u := range unmarshallers { t.Run(u.name, func(t *testing.T) { var r chan string err := u.u(b, &r) - ErrorContains(t, err, "type(chan) is unsupported") + ErrorContains(t, err, "chan is unsupported type") }) } }) @@ -1542,14 +1542,14 @@ func TestUnsupported(t *testing.T) { t.Run(m.name, func(t *testing.T) { var v func() _, err := m.m(v) - ErrorContains(t, err, "type(func) is unsupported") + ErrorContains(t, err, "func is unsupported type") }) } for _, u := range unmarshallers { t.Run(u.name, func(t *testing.T) { var r func() err := u.u(b, &r) - ErrorContains(t, err, "type(func) is unsupported") + ErrorContains(t, err, "func is unsupported type") }) } }) From ab6c756fed8660182020a89b07f65c037e0640c6 Mon Sep 17 00:00:00 2001 From: shamaton Date: Sat, 24 Aug 2024 14:22:28 +0900 Subject: [PATCH 33/41] add encoding struct tests --- internal/common/testutil/struct.go | 56 ++++ internal/stream/encoding/encoding_test.go | 8 +- internal/stream/encoding/struct_test.go | 332 ++++++++++++++++++++++ 3 files changed, 393 insertions(+), 3 deletions(-) create mode 100644 internal/common/testutil/struct.go create mode 100644 internal/stream/encoding/struct_test.go diff --git a/internal/common/testutil/struct.go b/internal/common/testutil/struct.go new file mode 100644 index 0000000..ab8b545 --- /dev/null +++ b/internal/common/testutil/struct.go @@ -0,0 +1,56 @@ +package testutil + +import ( + "math" + "reflect" + "strconv" + + "github.com/shamaton/msgpack/v2/def" +) + +// CreateStruct returns a struct that is made dynamically and encoded bytes. +func CreateStruct(fieldNum int) (v any, asMapBytes []byte, asArrayBytes []byte) { + if fieldNum < 0 { + panic("negative field number") + } + + fields := make([]reflect.StructField, 0, fieldNum) + asMapBytes = make([]byte, 0, fieldNum*2) + asArrayBytes = make([]byte, 0, fieldNum) + + for i := 0; i < fieldNum; i++ { + // create struct field + name := "A" + strconv.Itoa(i) + typ := reflect.TypeOf(1) + field := reflect.StructField{ + Name: name, + Type: typ, + Tag: `json:"B"`, + } + fields = append(fields, field) + + // set encoded bytes + if len(name) < 32 { + asMapBytes = append(asMapBytes, def.FixStr+byte(len(name))) + } else if len(name) < math.MaxUint8 { + asMapBytes = append(asMapBytes, def.Str8) + asMapBytes = append(asMapBytes, byte(len(name))) + } + for _, c := range name { + asMapBytes = append(asMapBytes, byte(c)) + } + value := byte(i % 0x7f) + asMapBytes = append(asMapBytes, value) + asArrayBytes = append(asArrayBytes, value) + } + t := reflect.StructOf(fields) + + // set field values + v = reflect.New(t).Interface() + rv := reflect.ValueOf(v) + for i := 0; i < rv.Elem().NumField(); i++ { + field := rv.Elem().Field(i) + field.SetInt(int64(i % 0x7f)) + } + return v, asMapBytes, asArrayBytes +} diff --git a/internal/stream/encoding/encoding_test.go b/internal/stream/encoding/encoding_test.go index dd983ca..3ad5134 100644 --- a/internal/stream/encoding/encoding_test.go +++ b/internal/stream/encoding/encoding_test.go @@ -42,6 +42,7 @@ type AsXXXTestCase[T any] struct { BufferSize int PreWriteSize int Error error + AsArray bool Method func(*encoder) func(T) error MethodForFixed func(*encoder) func(reflect.Value) (bool, error) MethodForStruct func(*encoder) func(reflect.Value) error @@ -79,9 +80,10 @@ func (tc *AsXXXTestCase[T]) Run(t *testing.T) { t.Run(tc.Name, func(t *testing.T) { w := NewTestWriter() e := encoder{ - w: w, - buf: common.GetBuffer(), - Common: common.Common{}, + w: w, + buf: common.GetBuffer(), + Common: common.Common{}, + asArray: tc.AsArray, } if tc.BufferSize < tc.PreWriteSize { diff --git a/internal/stream/encoding/struct_test.go b/internal/stream/encoding/struct_test.go new file mode 100644 index 0000000..25de8e2 --- /dev/null +++ b/internal/stream/encoding/struct_test.go @@ -0,0 +1,332 @@ +package encoding + +import ( + "math" + "reflect" + "testing" + "time" + + "github.com/shamaton/msgpack/v2/def" + tu "github.com/shamaton/msgpack/v2/internal/common/testutil" +) + +func Test_writeStruct(t *testing.T) { + method := func(e *encoder) func(reflect.Value) error { + return e.writeStruct + } + + t.Run("Ext", func(t *testing.T) { + value := time.Time{} + testcases := AsXXXTestCases[time.Time]{ + { + Name: "error", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForStruct: method, + }, + { + Name: "ok", + Value: value, + Expected: append([]byte{ + def.Ext8, + 12, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xf1, 0x88, 0x6e, 0x09, 0x00, + }), + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + t.Run("Array", func(t *testing.T) { + st, _, b := tu.CreateStruct(1) + value := reflect.ValueOf(st).Elem().Interface() + testcases := AsXXXTestCases[any]{ + { + Name: "error", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + AsArray: true, + MethodForStruct: method, + }, + { + Name: "ok", + Value: value, + Expected: append([]byte{def.FixArray + 1}, b...), + AsArray: true, + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + t.Run("Map", func(t *testing.T) { + st, b, _ := tu.CreateStruct(1) + value := reflect.ValueOf(st).Elem().Interface() + testcases := AsXXXTestCases[any]{ + { + Name: "error", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + AsArray: false, + MethodForStruct: method, + }, + { + Name: "ok", + Value: value, + Expected: append([]byte{def.FixMap + 1}, b...), + AsArray: false, + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) +} + +func Test_writeStructArray(t *testing.T) { + method := func(e *encoder) func(reflect.Value) error { + return e.writeStructArray + } + + t.Run("FixArray", func(t *testing.T) { + st, _, b := tu.CreateStruct(0x0f) + value := reflect.ValueOf(st).Elem().Interface() + testcases := AsXXXTestCases[any]{ + { + Name: "error.def", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForStruct: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 2, + PreWriteSize: 1, + Contains: []byte{def.FixArray + byte(0x0f)}, + MethodForStruct: method, + }, + { + Name: "ok", + Value: value, + Expected: append([]byte{def.FixArray + byte(0x0f)}, b...), + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + t.Run("Array16", func(t *testing.T) { + st, _, b := tu.CreateStruct(math.MaxUint16) + value := reflect.ValueOf(st).Elem().Interface() + testcases := AsXXXTestCases[any]{ + { + Name: "error.def", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForStruct: method, + }, + { + Name: "error.num", + Value: value, + BufferSize: 2, + PreWriteSize: 1, + Contains: []byte{def.Array16}, + MethodForStruct: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 4, + PreWriteSize: 1, + Contains: []byte{def.Array16, 0xff, 0xff}, + MethodForStruct: method, + }, + { + Name: "ok", + Value: value, + Expected: append([]byte{def.Array16, 0xff, 0xff}, b...), + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + t.Run("Array32", func(t *testing.T) { + st, _, b := tu.CreateStruct(math.MaxUint16 + 1) + value := reflect.ValueOf(st).Elem().Interface() + testcases := AsXXXTestCases[any]{ + { + Name: "error.def", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForStruct: method, + }, + { + Name: "error.num", + Value: value, + BufferSize: 2, + PreWriteSize: 1, + Contains: []byte{def.Array32}, + MethodForStruct: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 6, + PreWriteSize: 1, + Contains: []byte{def.Array32, 0x00, 0x01, 0x00, 0x00}, + MethodForStruct: method, + }, + { + Name: "ok", + Value: value, + Expected: append([]byte{def.Array32, 0x00, 0x01, 0x00, 0x00}, b...), + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) +} + +func Test_writeStructMap(t *testing.T) { + method := func(e *encoder) func(reflect.Value) error { + return e.writeStructMap + } + + t.Run("FixMap", func(t *testing.T) { + st, b, _ := tu.CreateStruct(0x0f) + value := reflect.ValueOf(st).Elem().Interface() + testcases := AsXXXTestCases[any]{ + { + Name: "error.def", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForStruct: method, + }, + { + Name: "error.key", + Value: value, + BufferSize: 2, + PreWriteSize: 1, + Contains: []byte{def.FixMap + byte(0x0f)}, + MethodForStruct: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 5, + PreWriteSize: 1, + Contains: append([]byte{def.FixMap + byte(0x0f)}, b[:3]...), + MethodForStruct: method, + }, + { + Name: "ok", + Value: value, + Expected: append([]byte{def.FixMap + byte(0x0f)}, b...), + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + t.Run("Map16", func(t *testing.T) { + st, b, _ := tu.CreateStruct(math.MaxUint16) + value := reflect.ValueOf(st).Elem().Interface() + testcases := AsXXXTestCases[any]{ + { + Name: "error.def", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForStruct: method, + }, + { + Name: "error.num", + Value: value, + BufferSize: 2, + PreWriteSize: 1, + Contains: []byte{def.Map16}, + MethodForStruct: method, + }, + { + Name: "error.key", + Value: value, + BufferSize: 4, + PreWriteSize: 1, + Contains: []byte{def.Map16, 0xff, 0xff}, + MethodForStruct: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 7, + PreWriteSize: 1, + Contains: append([]byte{def.Map16, 0xff, 0xff}, b[:3]...), + MethodForStruct: method, + }, + { + Name: "ok", + Value: value, + Expected: append([]byte{def.Map16, 0xff, 0xff}, b...), + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) + t.Run("Map32", func(t *testing.T) { + st, b, _ := tu.CreateStruct(math.MaxUint16 + 1) + value := reflect.ValueOf(st).Elem().Interface() + testcases := AsXXXTestCases[any]{ + { + Name: "error.def", + Value: value, + BufferSize: 1, + PreWriteSize: 1, + MethodForStruct: method, + }, + { + Name: "error.num", + Value: value, + BufferSize: 2, + PreWriteSize: 1, + Contains: []byte{def.Map32}, + MethodForStruct: method, + }, + { + Name: "error.key", + Value: value, + BufferSize: 6, + PreWriteSize: 1, + Contains: []byte{def.Map32, 0x00, 0x01, 0x00, 0x00}, + MethodForStruct: method, + }, + { + Name: "error.value", + Value: value, + BufferSize: 9, + PreWriteSize: 1, + Contains: append([]byte{def.Map32, 0x00, 0x01, 0x00, 0x00}, b[:3]...), + MethodForStruct: method, + }, + { + Name: "ok", + Value: value, + Expected: append([]byte{def.Map32, 0x00, 0x01, 0x00, 0x00}, b...), + BufferSize: 1, + MethodForStruct: method, + }, + } + testcases.Run(t) + }) +} From 5848d6acc6aa6c2dc7fdc8b173cf6c855d4084a7 Mon Sep 17 00:00:00 2001 From: shamaton Date: Sat, 24 Aug 2024 16:04:25 +0900 Subject: [PATCH 34/41] remove testutil from coverage --- .github/workflows/test.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bd7a5b5..12ff49e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -41,7 +41,13 @@ jobs: run: go build -v ./... - name: Test - run: go test -v --coverpkg=github.com/shamaton/msgpack/... --coverprofile=coverage.coverprofile --covermode=atomic ./... + run: go test -v --coverpkg=github.com/shamaton/msgpack/... --coverprofile=coverage.coverprofile.tmp --covermode=atomic ./... + + - name: Remove testutil from coverage + shell: bash + run: | + cat coverage.coverprofile.tmp | grep -v testutil > coverage.coverprofile + rm coverage.coverprofile.tmp - name: Upload coverage to Codecov if: success() && matrix.go == '1.22' && matrix.os == 'ubuntu-latest' From 80b3e7407e9d5d0765b9f0dd9eb69dfeca49ce3b Mon Sep 17 00:00:00 2001 From: shamaton Date: Sun, 25 Aug 2024 01:34:23 +0900 Subject: [PATCH 35/41] add encoding tests --- def/error.go | 1 + internal/encoding/byte.go | 2 +- internal/encoding/byte_test.go | 48 ++++++ internal/encoding/encoding.go | 4 +- internal/encoding/encoding_test.go | 225 +++++++++++++++++++++++++++++ internal/encoding/ext_test.go | 22 +++ internal/encoding/slice_test.go | 86 +++++++++++ internal/encoding/struct_test.go | 216 +++++++++++++++++++++++++++ 8 files changed, 601 insertions(+), 3 deletions(-) create mode 100644 internal/encoding/byte_test.go create mode 100644 internal/encoding/encoding_test.go create mode 100644 internal/encoding/ext_test.go create mode 100644 internal/encoding/slice_test.go create mode 100644 internal/encoding/struct_test.go diff --git a/def/error.go b/def/error.go index d57b404..b1910c3 100644 --- a/def/error.go +++ b/def/error.go @@ -15,4 +15,5 @@ var ( ErrLackDataLengthToSlice = errors.New("data length lacks to create slice") ErrLackDataLengthToMap = errors.New("data length lacks to create map") ErrUnsupported = errors.New("unsupported") + ErrNotMatchLastIndex = errors.New("not match last index") ) diff --git a/internal/encoding/byte.go b/internal/encoding/byte.go index 976bdff..67aaceb 100644 --- a/internal/encoding/byte.go +++ b/internal/encoding/byte.go @@ -23,7 +23,7 @@ func (e *encoder) calcByteSlice(l int) (int, error) { return def.Byte4 + l, nil } // not supported error - return 0, fmt.Errorf("not support this array length : %d", l) + return 0, fmt.Errorf("%w slice length : %d", def.ErrUnsupported, l) } func (e *encoder) writeByteSliceLength(l int, offset int) int { diff --git a/internal/encoding/byte_test.go b/internal/encoding/byte_test.go new file mode 100644 index 0000000..e7ebf9d --- /dev/null +++ b/internal/encoding/byte_test.go @@ -0,0 +1,48 @@ +package encoding + +import ( + "math" + "testing" + + "github.com/shamaton/msgpack/v2/def" + + tu "github.com/shamaton/msgpack/v2/internal/common/testutil" +) + +func Test_calcByteSlice(t *testing.T) { + testcases := []struct { + name string + value int + result int + error error + }{ + { + name: "u8", + value: math.MaxUint8, + result: def.Byte1 + math.MaxUint8, + }, + { + name: "u16", + value: math.MaxUint16, + result: def.Byte2 + math.MaxUint16, + }, + { + name: "u32", + value: math.MaxUint32, + result: def.Byte4 + math.MaxUint32, + }, + { + name: "u32over", + value: math.MaxUint32 + 1, + error: def.ErrUnsupported, + }, + } + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + e := encoder{} + result, err := e.calcByteSlice(tc.value) + tu.IsError(t, err, tc.error) + tu.Equal(t, result, tc.result) + }) + } +} diff --git a/internal/encoding/encoding.go b/internal/encoding/encoding.go index 51e1618..33a120f 100644 --- a/internal/encoding/encoding.go +++ b/internal/encoding/encoding.go @@ -45,7 +45,7 @@ func Encode(v interface{}, asArray bool) (b []byte, err error) { e.d = make([]byte, size) last := e.create(rv, 0) if size != last { - return nil, fmt.Errorf("failed serialization size=%d, lastIdx=%d", size, last) + return nil, fmt.Errorf("%w size=%d, lastIdx=%d", def.ErrNotMatchLastIndex, size, last) } return e.d, err } @@ -116,7 +116,7 @@ func (e *encoder) calcSize(rv reflect.Value) (int, error) { ret += def.Byte4 } else { // not supported error - return 0, fmt.Errorf("not support this array length : %d", l) + return 0, fmt.Errorf("%w array length : %d", def.ErrUnsupported, l) } if size, find := e.calcFixedSlice(rv); find { diff --git a/internal/encoding/encoding_test.go b/internal/encoding/encoding_test.go new file mode 100644 index 0000000..d3e12a1 --- /dev/null +++ b/internal/encoding/encoding_test.go @@ -0,0 +1,225 @@ +package encoding + +import ( + "math" + "reflect" + "strconv" + "testing" + + "github.com/shamaton/msgpack/v2/def" + + tu "github.com/shamaton/msgpack/v2/internal/common/testutil" +) + +func TestEncode(t *testing.T) { + v := 1 + vv := &v + + b, err := Encode(&vv, false) + tu.NoError(t, err) + + tu.EqualSlice(t, b, []byte{def.PositiveFixIntMin + 1}) +} + +func Test_encode(t *testing.T) { + type st struct { + V int + } + + type testcase struct { + value any + code byte + error error + } + + f := func(tcs []testcase, t *testing.T) { + for _, tc := range tcs { + rv := reflect.ValueOf(tc.value) + t.Run(rv.Type().String(), func(t *testing.T) { + e := encoder{} + size, err := e.calcSize(rv) + tu.IsError(t, err, tc.error) + if err != nil { + return + } + + e.d = make([]byte, size) + result := e.create(rv, 0) + tu.Equal(t, result, size) + tu.Equal(t, e.d[0], tc.code) + }) + } + } + + var testcases []testcase + + // slice tests + testcases = []testcase{ + { + value: ([]byte)(nil), + code: def.Nil, + }, + { + value: make([]byte, math.MaxUint32+1), + error: def.ErrUnsupported, + }, + { + value: make([]int, 1), + code: def.FixArray + 1, + }, + { + value: make([]int, math.MaxUint16), + code: def.Array16, + }, + // too heavy + //{ + // value: make([]int, math.MaxUint32), + // code: def.Array32, + //}, + //{ + // value: make([]int, math.MaxUint32+1), + // error: def.ErrUnsupported, + //}, + { + value: []st{{1}}, + code: def.FixArray + 1, + }, + { + value: []chan int{make(chan int)}, + error: def.ErrUnsupported, + }, + } + t.Run("slice", func(t *testing.T) { + f(testcases, t) + }) + + // array tests + testcases = []testcase{ + // stack frame too large (compile error) + //{ + // value: [math.MaxUint32 + 1]byte{}, + // error: def.ErrUnsupported, + //}, + { + value: [1]int{}, + code: def.FixArray + 1, + }, + { + value: [math.MaxUint16]int{}, + code: def.Array16, + }, + // stack frame too large (compile error) + //{ + // value: [math.MaxUint32]int{}, + // code: def.Array32, + //}, + //{ + // value: [math.MaxUint32 + 1]int{}, + // error: def.ErrUnsupported, + //}, + { + value: [1]st{{1}}, + code: def.FixArray + 1, + }, + { + value: [1]chan int{make(chan int)}, + error: def.ErrUnsupported, + }, + } + t.Run("array", func(t *testing.T) { + f(testcases, t) + }) + + // map tests + createMap := func(l int) map[string]int { + m := map[string]int{} + for i := 0; i < l; i++ { + m[strconv.Itoa(i)] = i + } + return m + } + testcases = []testcase{ + { + value: (map[string]int)(nil), + code: def.Nil, + }, + { + value: createMap(1), + code: def.FixMap + 1, + }, + { + value: createMap(math.MaxUint16), + code: def.Map16, + }, + // too heavy + //{ + // value: createMap(math.MaxUint32), + // code: def.Map32, + //}, + //{ + // value: createMap(math.MaxUint32 + 1), + // error: def.ErrUnsupported, + //}, + { + value: map[chan int]int{make(chan int): 1}, + error: def.ErrUnsupported, + }, + { + value: map[string]chan int{"a": make(chan int)}, + error: def.ErrUnsupported, + }, + } + t.Run("map", func(t *testing.T) { + f(testcases, t) + }) + + type unsupport struct { + Chan chan int + } + + testcases = []testcase{ + { + value: unsupport{make(chan int)}, + error: def.ErrUnsupported, + }, + } + t.Run("struct", func(t *testing.T) { + f(testcases, t) + }) + + ch := make(chan int) + testcases = []testcase{ + { + value: (*int)(nil), + code: def.Nil, + }, + { + value: &ch, + error: def.ErrUnsupported, + }, + { + value: new(int), + code: 0, + }, + } + t.Run("ptr", func(t *testing.T) { + f(testcases, t) + }) + + type inter struct { + V any + } + testcases = []testcase{ + { + value: inter{V: make(chan int)}, + error: def.ErrUnsupported, + }, + { + value: inter{V: 1}, + code: def.FixMap + 1, + }, + } + t.Run("interface", func(t *testing.T) { + f(testcases, t) + }) +} diff --git a/internal/encoding/ext_test.go b/internal/encoding/ext_test.go new file mode 100644 index 0000000..2d70119 --- /dev/null +++ b/internal/encoding/ext_test.go @@ -0,0 +1,22 @@ +package encoding + +import ( + "testing" + + tu "github.com/shamaton/msgpack/v2/internal/common/testutil" + "github.com/shamaton/msgpack/v2/time" +) + +func Test_AddExtEncoder(t *testing.T) { + t.Run("ignore", func(t *testing.T) { + AddExtEncoder(time.Encoder) + tu.Equal(t, len(extCoders), 1) + }) +} + +func Test_RemoveExtEncoder(t *testing.T) { + t.Run("ignore", func(t *testing.T) { + RemoveExtEncoder(time.Encoder) + tu.Equal(t, len(extCoders), 1) + }) +} diff --git a/internal/encoding/slice_test.go b/internal/encoding/slice_test.go new file mode 100644 index 0000000..b30d6a5 --- /dev/null +++ b/internal/encoding/slice_test.go @@ -0,0 +1,86 @@ +package encoding + +import ( + "reflect" + "testing" + + tu "github.com/shamaton/msgpack/v2/internal/common/testutil" +) + +func Test_FixedSlice(t *testing.T) { + testcases := []struct { + value any + size int + }{ + { + value: []int{-1}, + size: 1, + }, + { + value: []uint{1}, + size: 1, + }, + { + value: []string{"a"}, + size: 2, + }, + { + value: []float32{1.23}, + size: 5, + }, + { + value: []float64{1.23}, + size: 9, + }, + { + value: []bool{true}, + size: 1, + }, + { + value: []int8{1}, + size: 1, + }, + { + value: []int16{1}, + size: 1, + }, + { + value: []int32{1}, + size: 1, + }, + { + value: []int64{1}, + size: 1, + }, + { + value: []uint8{1}, + size: 1, + }, + { + value: []uint16{1}, + size: 1, + }, + { + value: []uint32{1}, + size: 1, + }, + { + value: []uint64{1}, + size: 1, + }, + } + for _, tc := range testcases { + rv := reflect.ValueOf(tc.value) + t.Run(rv.Type().String(), func(t *testing.T) { + e := encoder{} + size, b := e.calcFixedSlice(rv) + tu.Equal(t, b, true) + tu.Equal(t, size, tc.size) + + e.d = make([]byte, size) + result, b := e.writeFixedSlice(rv, 0) + tu.Equal(t, b, true) + tu.Equal(t, result, size) + }) + } +} diff --git a/internal/encoding/struct_test.go b/internal/encoding/struct_test.go new file mode 100644 index 0000000..3310d74 --- /dev/null +++ b/internal/encoding/struct_test.go @@ -0,0 +1,216 @@ +package encoding + +import ( + "math" + "reflect" + "testing" + + "github.com/shamaton/msgpack/v2/def" + tu "github.com/shamaton/msgpack/v2/internal/common/testutil" +) + +func Test_calcStructArray(t *testing.T) { + type b struct { + B []byte + } + + t.Run("non-cache", func(t *testing.T) { + value := b{B: make([]byte, math.MaxUint32+1)} + e := encoder{} + rv := reflect.ValueOf(value) + _, err := e.calcStructArray(rv) + tu.IsError(t, err, def.ErrUnsupported) + }) + t.Run("cache", func(t *testing.T) { + value := b{B: make([]byte, 1)} + e := encoder{} + rv := reflect.ValueOf(value) + _, err := e.calcStructArray(rv) + tu.NoError(t, err) + + value = b{B: make([]byte, math.MaxUint32+1)} + rv = reflect.ValueOf(value) + _, err = e.calcStructArray(rv) + tu.IsError(t, err, def.ErrUnsupported) + }) + + testcases := []struct { + name string + value int + result int + error error + }{ + { + name: "0x0f", + value: 0x0f, + result: 0, + }, + { + name: "u16", + value: math.MaxUint16, + result: def.Byte2, + }, + { + name: "u32", + value: math.MaxUint16 + 1, + result: def.Byte4, + }, + // can not run by out of memory + //{ + // name: "u32over", + // value: math.MaxUint32 + 1, + // result: 0, + //}, + } + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + e := encoder{} + v, _, bs := tu.CreateStruct(tc.value) + rv := reflect.ValueOf(v).Elem() + result, err := e.calcStructArray(rv) + tu.IsError(t, err, tc.error) + tu.Equal(t, result, tc.result+len(bs)) + }) + } +} + +func Test_calcStructMap(t *testing.T) { + type b struct { + B []byte + } + + t.Run("non-cache", func(t *testing.T) { + value := b{B: make([]byte, math.MaxUint32+1)} + e := encoder{} + rv := reflect.ValueOf(value) + _, err := e.calcStructMap(rv) + tu.IsError(t, err, def.ErrUnsupported) + }) + t.Run("cache", func(t *testing.T) { + value := b{B: make([]byte, 1)} + e := encoder{} + rv := reflect.ValueOf(value) + _, err := e.calcStructMap(rv) + tu.NoError(t, err) + + value = b{B: make([]byte, math.MaxUint32+1)} + rv = reflect.ValueOf(value) + _, err = e.calcStructMap(rv) + tu.IsError(t, err, def.ErrUnsupported) + }) + + testcases := []struct { + name string + value int + result int + error error + }{ + { + name: "0x0f", + value: 0x0f, + result: 0, + }, + { + name: "u16", + value: math.MaxUint16, + result: def.Byte2, + }, + { + name: "u32", + value: math.MaxUint16 + 1, + result: def.Byte4, + }, + // can not run by out of memory + //{ + // name: "u32over", + // value: math.MaxUint32 + 1, + // result: 0, + //}, + } + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + e := encoder{} + v, bs, _ := tu.CreateStruct(tc.value) + rv := reflect.ValueOf(v).Elem() + result, err := e.calcStructMap(rv) + tu.IsError(t, err, tc.error) + tu.Equal(t, result, tc.result+len(bs)) + }) + } +} + +func Test_writeStructArray(t *testing.T) { + testcases := []struct { + name string + value int + code byte + }{ + { + name: "0x0f", + value: 0x0f, + code: def.FixArray + 0x0f, + }, + { + name: "u16", + value: math.MaxUint16, + code: def.Array16, + }, + { + name: "u32", + value: math.MaxUint16 + 1, + code: def.Array32, + }, + } + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + e := encoder{} + v, _, _ := tu.CreateStruct(tc.value) + rv := reflect.ValueOf(v).Elem() + size, err := e.calcStructArray(rv) + tu.NoError(t, err) + + e.d = make([]byte, size+def.Byte1) + result := e.writeStructArray(rv, 0) + tu.Equal(t, len(e.d), result) + tu.Equal(t, e.d[0], tc.code) + }) + } +} + +func Test_writeStructMap(t *testing.T) { + testcases := []struct { + name string + value int + code byte + }{ + { + name: "0x0f", + value: 0x0f, + code: def.FixMap + 0x0f, + }, + { + name: "u16", + value: math.MaxUint16, + code: def.Map16, + }, + { + name: "u32", + value: math.MaxUint16 + 1, + code: def.Map32, + }, + } + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + e := encoder{} + v, _, _ := tu.CreateStruct(tc.value) + rv := reflect.ValueOf(v).Elem() + size, err := e.calcStructMap(rv) + tu.NoError(t, err) + + e.d = make([]byte, size+def.Byte1) + result := e.writeStructMap(rv, 0) + tu.Equal(t, len(e.d), result) + tu.Equal(t, e.d[0], tc.code) + }) + } +} From fbac5d3ccb849474a8b9816119f8302b066252ff Mon Sep 17 00:00:00 2001 From: shamaton Date: Sun, 25 Aug 2024 22:59:49 +0900 Subject: [PATCH 36/41] add common test --- internal/common/common_test.go | 48 ++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 internal/common/common_test.go diff --git a/internal/common/common_test.go b/internal/common/common_test.go new file mode 100644 index 0000000..b1c0e30 --- /dev/null +++ b/internal/common/common_test.go @@ -0,0 +1,48 @@ +package common + +import ( + "reflect" + "testing" + + tu "github.com/shamaton/msgpack/v2/internal/common/testutil" +) + +func TestCommon_CheckField(t *testing.T) { + common := Common{} + + t.Run("tag:-", func(t *testing.T) { + field := reflect.StructField{ + Name: "A", + Tag: `msgpack:"-"`, + } + b, v := common.CheckField(field) + tu.Equal(t, b, false) + tu.Equal(t, v, "") + }) + t.Run("tag:B", func(t *testing.T) { + field := reflect.StructField{ + Name: "A", + Tag: `msgpack:"B"`, + } + b, v := common.CheckField(field) + tu.Equal(t, b, true) + tu.Equal(t, v, "B") + }) + t.Run("name:A", func(t *testing.T) { + field := reflect.StructField{ + Name: "A", + Tag: `msgpack:""`, + } + b, v := common.CheckField(field) + tu.Equal(t, b, true) + tu.Equal(t, v, "A") + }) + t.Run("private", func(t *testing.T) { + field := reflect.StructField{ + Name: "a", + } + b, v := common.CheckField(field) + tu.Equal(t, b, false) + tu.Equal(t, v, "") + }) +} From dbef5c6c73f6dea65a9b7e3a4f993ad05ba53fd5 Mon Sep 17 00:00:00 2001 From: shamaton Date: Sun, 25 Aug 2024 23:49:48 +0900 Subject: [PATCH 37/41] add msgpack.Error --- def/error.go | 37 ++++++++++++++++++++++++------------- errors.go | 8 ++++++++ 2 files changed, 32 insertions(+), 13 deletions(-) create mode 100644 errors.go diff --git a/def/error.go b/def/error.go index b1910c3..0352455 100644 --- a/def/error.go +++ b/def/error.go @@ -1,19 +1,30 @@ package def -import "errors" +import ( + "errors" + "fmt" +) var ( - ErrNoData = errors.New("no data") - ErrHasLeftOver = errors.New("data has left over") - ErrReceiverNotPointer = errors.New("receiver not pointer") - ErrNotMatchArrayElement = errors.New("not match array element") - ErrCanNotDecode = errors.New("msgpack : invalid code") - ErrCanNotSetSliceAsMapKey = errors.New("can not set slice as map key") - ErrCanNotSetMapAsMapKey = errors.New("can not set map as map key") + // base errors + + ErrMsgpack = errors.New("") + + // decoding errors + + ErrNoData = fmt.Errorf("%wno data", ErrMsgpack) + ErrHasLeftOver = fmt.Errorf("%wdata has left over", ErrMsgpack) + ErrReceiverNotPointer = fmt.Errorf("%wreceiver not pointer", ErrMsgpack) + ErrNotMatchArrayElement = fmt.Errorf("%wnot match array element", ErrMsgpack) + ErrCanNotDecode = fmt.Errorf("%winvalid code", ErrMsgpack) + ErrCanNotSetSliceAsMapKey = fmt.Errorf("%wcan not set slice as map key", ErrMsgpack) + ErrCanNotSetMapAsMapKey = fmt.Errorf("%wcan not set map as map key", ErrMsgpack) + + // encoding errors - ErrTooShortBytes = errors.New("too short bytes") - ErrLackDataLengthToSlice = errors.New("data length lacks to create slice") - ErrLackDataLengthToMap = errors.New("data length lacks to create map") - ErrUnsupported = errors.New("unsupported") - ErrNotMatchLastIndex = errors.New("not match last index") + ErrTooShortBytes = fmt.Errorf("%wtoo short bytes", ErrMsgpack) + ErrLackDataLengthToSlice = fmt.Errorf("%wdata length lacks to create slice", ErrMsgpack) + ErrLackDataLengthToMap = fmt.Errorf("%wdata length lacks to create map", ErrMsgpack) + ErrUnsupported = fmt.Errorf("%wunsupported type", ErrMsgpack) + ErrNotMatchLastIndex = fmt.Errorf("%wnot match last index", ErrMsgpack) ) diff --git a/errors.go b/errors.go new file mode 100644 index 0000000..59e7916 --- /dev/null +++ b/errors.go @@ -0,0 +1,8 @@ +package msgpack + +import ( + "github.com/shamaton/msgpack/v2/def" +) + +// Error is used in all msgpack error as the based error. +var Error = def.ErrMsgpack From 90d8baa825061d0f5ef24d9697053a6724cd2ef1 Mon Sep 17 00:00:00 2001 From: shamaton Date: Sun, 25 Aug 2024 23:53:12 +0900 Subject: [PATCH 38/41] change from ErrUnsupported to ErrUnsupportedType --- def/error.go | 2 +- internal/decoding/decoding.go | 2 +- internal/encoding/byte.go | 2 +- internal/encoding/byte_test.go | 2 +- internal/encoding/encoding.go | 4 ++-- internal/encoding/encoding_test.go | 24 +++++++++++------------ internal/encoding/struct_test.go | 8 ++++---- internal/stream/decoding/decoding.go | 2 +- internal/stream/encoding/encoding.go | 2 +- internal/stream/encoding/encoding_test.go | 2 +- 10 files changed, 25 insertions(+), 25 deletions(-) diff --git a/def/error.go b/def/error.go index 0352455..87032a1 100644 --- a/def/error.go +++ b/def/error.go @@ -25,6 +25,6 @@ var ( ErrTooShortBytes = fmt.Errorf("%wtoo short bytes", ErrMsgpack) ErrLackDataLengthToSlice = fmt.Errorf("%wdata length lacks to create slice", ErrMsgpack) ErrLackDataLengthToMap = fmt.Errorf("%wdata length lacks to create map", ErrMsgpack) - ErrUnsupported = fmt.Errorf("%wunsupported type", ErrMsgpack) + ErrUnsupportedType = fmt.Errorf("%wunsupported type", ErrMsgpack) ErrNotMatchLastIndex = fmt.Errorf("%wnot match last index", ErrMsgpack) ) diff --git a/internal/decoding/decoding.go b/internal/decoding/decoding.go index 1ceba9b..0d569f4 100644 --- a/internal/decoding/decoding.go +++ b/internal/decoding/decoding.go @@ -333,7 +333,7 @@ func (d *decoder) decode(rv reflect.Value, offset int) (int, error) { } default: - return 0, fmt.Errorf("%v is %w type", rv.Kind(), def.ErrUnsupported) + return 0, fmt.Errorf("%v is %w type", rv.Kind(), def.ErrUnsupportedType) } return offset, nil } diff --git a/internal/encoding/byte.go b/internal/encoding/byte.go index 67aaceb..c1bb107 100644 --- a/internal/encoding/byte.go +++ b/internal/encoding/byte.go @@ -23,7 +23,7 @@ func (e *encoder) calcByteSlice(l int) (int, error) { return def.Byte4 + l, nil } // not supported error - return 0, fmt.Errorf("%w slice length : %d", def.ErrUnsupported, l) + return 0, fmt.Errorf("%w slice length : %d", def.ErrUnsupportedType, l) } func (e *encoder) writeByteSliceLength(l int, offset int) int { diff --git a/internal/encoding/byte_test.go b/internal/encoding/byte_test.go index e7ebf9d..1c9400c 100644 --- a/internal/encoding/byte_test.go +++ b/internal/encoding/byte_test.go @@ -34,7 +34,7 @@ func Test_calcByteSlice(t *testing.T) { { name: "u32over", value: math.MaxUint32 + 1, - error: def.ErrUnsupported, + error: def.ErrUnsupportedType, }, } for _, tc := range testcases { diff --git a/internal/encoding/encoding.go b/internal/encoding/encoding.go index 33a120f..0d9b2ca 100644 --- a/internal/encoding/encoding.go +++ b/internal/encoding/encoding.go @@ -116,7 +116,7 @@ func (e *encoder) calcSize(rv reflect.Value) (int, error) { ret += def.Byte4 } else { // not supported error - return 0, fmt.Errorf("%w array length : %d", def.ErrUnsupported, l) + return 0, fmt.Errorf("%w array length : %d", def.ErrUnsupportedType, l) } if size, find := e.calcFixedSlice(rv); find { @@ -262,7 +262,7 @@ func (e *encoder) calcSize(rv reflect.Value) (int, error) { // do nothing (return nil) default: - return 0, fmt.Errorf("%v is %w type", rv.Kind(), def.ErrUnsupported) + return 0, fmt.Errorf("%v is %w type", rv.Kind(), def.ErrUnsupportedType) } return ret, nil diff --git a/internal/encoding/encoding_test.go b/internal/encoding/encoding_test.go index d3e12a1..f0942c6 100644 --- a/internal/encoding/encoding_test.go +++ b/internal/encoding/encoding_test.go @@ -61,7 +61,7 @@ func Test_encode(t *testing.T) { }, { value: make([]byte, math.MaxUint32+1), - error: def.ErrUnsupported, + error: def.ErrUnsupportedType, }, { value: make([]int, 1), @@ -78,7 +78,7 @@ func Test_encode(t *testing.T) { //}, //{ // value: make([]int, math.MaxUint32+1), - // error: def.ErrUnsupported, + // error: def.ErrUnsupportedType, //}, { value: []st{{1}}, @@ -86,7 +86,7 @@ func Test_encode(t *testing.T) { }, { value: []chan int{make(chan int)}, - error: def.ErrUnsupported, + error: def.ErrUnsupportedType, }, } t.Run("slice", func(t *testing.T) { @@ -98,7 +98,7 @@ func Test_encode(t *testing.T) { // stack frame too large (compile error) //{ // value: [math.MaxUint32 + 1]byte{}, - // error: def.ErrUnsupported, + // error: def.ErrUnsupportedType, //}, { value: [1]int{}, @@ -115,7 +115,7 @@ func Test_encode(t *testing.T) { //}, //{ // value: [math.MaxUint32 + 1]int{}, - // error: def.ErrUnsupported, + // error: def.ErrUnsupportedType, //}, { value: [1]st{{1}}, @@ -123,7 +123,7 @@ func Test_encode(t *testing.T) { }, { value: [1]chan int{make(chan int)}, - error: def.ErrUnsupported, + error: def.ErrUnsupportedType, }, } t.Run("array", func(t *testing.T) { @@ -158,15 +158,15 @@ func Test_encode(t *testing.T) { //}, //{ // value: createMap(math.MaxUint32 + 1), - // error: def.ErrUnsupported, + // error: def.ErrUnsupportedType, //}, { value: map[chan int]int{make(chan int): 1}, - error: def.ErrUnsupported, + error: def.ErrUnsupportedType, }, { value: map[string]chan int{"a": make(chan int)}, - error: def.ErrUnsupported, + error: def.ErrUnsupportedType, }, } t.Run("map", func(t *testing.T) { @@ -180,7 +180,7 @@ func Test_encode(t *testing.T) { testcases = []testcase{ { value: unsupport{make(chan int)}, - error: def.ErrUnsupported, + error: def.ErrUnsupportedType, }, } t.Run("struct", func(t *testing.T) { @@ -195,7 +195,7 @@ func Test_encode(t *testing.T) { }, { value: &ch, - error: def.ErrUnsupported, + error: def.ErrUnsupportedType, }, { value: new(int), @@ -212,7 +212,7 @@ func Test_encode(t *testing.T) { testcases = []testcase{ { value: inter{V: make(chan int)}, - error: def.ErrUnsupported, + error: def.ErrUnsupportedType, }, { value: inter{V: 1}, diff --git a/internal/encoding/struct_test.go b/internal/encoding/struct_test.go index 3310d74..7ca0c13 100644 --- a/internal/encoding/struct_test.go +++ b/internal/encoding/struct_test.go @@ -19,7 +19,7 @@ func Test_calcStructArray(t *testing.T) { e := encoder{} rv := reflect.ValueOf(value) _, err := e.calcStructArray(rv) - tu.IsError(t, err, def.ErrUnsupported) + tu.IsError(t, err, def.ErrUnsupportedType) }) t.Run("cache", func(t *testing.T) { value := b{B: make([]byte, 1)} @@ -31,7 +31,7 @@ func Test_calcStructArray(t *testing.T) { value = b{B: make([]byte, math.MaxUint32+1)} rv = reflect.ValueOf(value) _, err = e.calcStructArray(rv) - tu.IsError(t, err, def.ErrUnsupported) + tu.IsError(t, err, def.ErrUnsupportedType) }) testcases := []struct { @@ -84,7 +84,7 @@ func Test_calcStructMap(t *testing.T) { e := encoder{} rv := reflect.ValueOf(value) _, err := e.calcStructMap(rv) - tu.IsError(t, err, def.ErrUnsupported) + tu.IsError(t, err, def.ErrUnsupportedType) }) t.Run("cache", func(t *testing.T) { value := b{B: make([]byte, 1)} @@ -96,7 +96,7 @@ func Test_calcStructMap(t *testing.T) { value = b{B: make([]byte, math.MaxUint32+1)} rv = reflect.ValueOf(value) _, err = e.calcStructMap(rv) - tu.IsError(t, err, def.ErrUnsupported) + tu.IsError(t, err, def.ErrUnsupportedType) }) testcases := []struct { diff --git a/internal/stream/decoding/decoding.go b/internal/stream/decoding/decoding.go index cb5288e..8a416c8 100644 --- a/internal/stream/decoding/decoding.go +++ b/internal/stream/decoding/decoding.go @@ -314,7 +314,7 @@ func (d *decoder) decodeWithCode(code byte, rv reflect.Value) error { } default: - return fmt.Errorf("%v is %w type", rv.Kind(), def.ErrUnsupported) + return fmt.Errorf("%v is %w type", rv.Kind(), def.ErrUnsupportedType) } return nil } diff --git a/internal/stream/encoding/encoding.go b/internal/stream/encoding/encoding.go index 2613f37..d53d9f4 100644 --- a/internal/stream/encoding/encoding.go +++ b/internal/stream/encoding/encoding.go @@ -189,7 +189,7 @@ func (e *encoder) create(rv reflect.Value) error { case reflect.Invalid: return e.writeNil() default: - return fmt.Errorf("%v is %w type", rv.Kind(), def.ErrUnsupported) + return fmt.Errorf("%v is %w type", rv.Kind(), def.ErrUnsupportedType) } return nil } diff --git a/internal/stream/encoding/encoding_test.go b/internal/stream/encoding/encoding_test.go index 3ad5134..0acccfb 100644 --- a/internal/stream/encoding/encoding_test.go +++ b/internal/stream/encoding/encoding_test.go @@ -890,7 +890,7 @@ func Test_create(t *testing.T) { Name: "ok", Value: value, BufferSize: 1, - Error: def.ErrUnsupported, + Error: def.ErrUnsupportedType, MethodForStruct: method, }, } From ea02dc3e87cbb41046f9e751c91d0ff0250edc06 Mon Sep 17 00:00:00 2001 From: shamaton Date: Sun, 25 Aug 2024 23:59:22 +0900 Subject: [PATCH 39/41] add def.ErrUnsupportedLength as a error definition --- def/error.go | 1 + internal/encoding/encoding.go | 4 ++-- internal/encoding/struct.go | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/def/error.go b/def/error.go index 87032a1..5bdcf8d 100644 --- a/def/error.go +++ b/def/error.go @@ -26,5 +26,6 @@ var ( ErrLackDataLengthToSlice = fmt.Errorf("%wdata length lacks to create slice", ErrMsgpack) ErrLackDataLengthToMap = fmt.Errorf("%wdata length lacks to create map", ErrMsgpack) ErrUnsupportedType = fmt.Errorf("%wunsupported type", ErrMsgpack) + ErrUnsupportedLength = fmt.Errorf("%wunsupported length", ErrMsgpack) ErrNotMatchLastIndex = fmt.Errorf("%wnot match last index", ErrMsgpack) ) diff --git a/internal/encoding/encoding.go b/internal/encoding/encoding.go index 0d9b2ca..1d39310 100644 --- a/internal/encoding/encoding.go +++ b/internal/encoding/encoding.go @@ -164,7 +164,7 @@ func (e *encoder) calcSize(rv reflect.Value) (int, error) { ret += def.Byte4 } else { // not supported error - return 0, fmt.Errorf("not support this array length : %d", l) + return 0, fmt.Errorf("array length %d is %w", l, def.ErrUnsupportedLength) } // func @@ -201,7 +201,7 @@ func (e *encoder) calcSize(rv reflect.Value) (int, error) { ret += def.Byte4 } else { // not supported error - return 0, fmt.Errorf("not support this map length : %d", l) + return 0, fmt.Errorf("map length %d is %w", l, def.ErrUnsupportedLength) } if size, find := e.calcFixedMap(rv); find { diff --git a/internal/encoding/struct.go b/internal/encoding/struct.go index 793f1be..1dddf04 100644 --- a/internal/encoding/struct.go +++ b/internal/encoding/struct.go @@ -95,7 +95,7 @@ func (e *encoder) calcStructArray(rv reflect.Value) (int, error) { ret += def.Byte4 } else { // not supported error - return 0, fmt.Errorf("not support this array length : %d", l) + return 0, fmt.Errorf("array length %d is %w", l, def.ErrUnsupportedLength) } return ret, nil } @@ -142,7 +142,7 @@ func (e *encoder) calcStructMap(rv reflect.Value) (int, error) { ret += def.Byte4 } else { // not supported error - return 0, fmt.Errorf("not support this array length : %d", l) + return 0, fmt.Errorf("map length %d is %w", l, def.ErrUnsupportedLength) } return ret, nil } From 69ee43d7478c70bc4fb991f92df79dad9fbeebde Mon Sep 17 00:00:00 2001 From: shamaton Date: Mon, 26 Aug 2024 23:47:29 +0900 Subject: [PATCH 40/41] fix testcase logic in stream decoding --- internal/decoding/decoding.go | 2 +- internal/stream/decoding/bin_test.go | 2 +- internal/stream/decoding/bool_test.go | 2 +- internal/stream/decoding/complex_test.go | 4 +- internal/stream/decoding/decoding.go | 2 +- internal/stream/decoding/decoding_test.go | 6 - internal/stream/decoding/ext_test.go | 2 - internal/stream/decoding/float_test.go | 7 +- internal/stream/decoding/interface_test.go | 2 +- internal/stream/decoding/map_test.go | 288 ++++++++++----------- msgpack_test.go | 3 +- 11 files changed, 155 insertions(+), 165 deletions(-) diff --git a/internal/decoding/decoding.go b/internal/decoding/decoding.go index 0d569f4..e81c174 100644 --- a/internal/decoding/decoding.go +++ b/internal/decoding/decoding.go @@ -339,5 +339,5 @@ func (d *decoder) decode(rv reflect.Value, offset int) (int, error) { } func (d *decoder) errorTemplate(code byte, k reflect.Kind) error { - return fmt.Errorf("msgpack : invalid code %x decoding %v, %w", code, k, def.ErrCanNotDecode) + return fmt.Errorf("%w %x decoding as %v", def.ErrCanNotDecode, code, k) } diff --git a/internal/stream/decoding/bin_test.go b/internal/stream/decoding/bin_test.go index e205109..50a4c4a 100644 --- a/internal/stream/decoding/bin_test.go +++ b/internal/stream/decoding/bin_test.go @@ -92,7 +92,7 @@ func Test_asBinWithCode(t *testing.T) { { Name: "Unexpected", Code: def.Nil, - IsTemplateError: true, + Error: def.ErrCanNotDecode, MethodAsWithCode: method, }, } diff --git a/internal/stream/decoding/bool_test.go b/internal/stream/decoding/bool_test.go index c06d709..72f9ec3 100644 --- a/internal/stream/decoding/bool_test.go +++ b/internal/stream/decoding/bool_test.go @@ -53,7 +53,7 @@ func Test_asBoolWithCode(t *testing.T) { { Name: "Unexpected", Code: def.Nil, - IsTemplateError: true, + Error: def.ErrCanNotDecode, MethodAsWithCode: method, }, } diff --git a/internal/stream/decoding/complex_test.go b/internal/stream/decoding/complex_test.go index 79317bd..4c541ed 100644 --- a/internal/stream/decoding/complex_test.go +++ b/internal/stream/decoding/complex_test.go @@ -77,7 +77,7 @@ func Test_asComplex64(t *testing.T) { { Name: "Unexpected", Code: def.Nil, - IsTemplateError: true, + Error: def.ErrCanNotDecode, MethodAsWithCode: method, }, } @@ -155,7 +155,7 @@ func Test_asComplex128(t *testing.T) { { Name: "Unexpected", Code: def.Nil, - IsTemplateError: true, + Error: def.ErrCanNotDecode, MethodAsWithCode: method, }, } diff --git a/internal/stream/decoding/decoding.go b/internal/stream/decoding/decoding.go index 8a416c8..d2e452c 100644 --- a/internal/stream/decoding/decoding.go +++ b/internal/stream/decoding/decoding.go @@ -320,5 +320,5 @@ func (d *decoder) decodeWithCode(code byte, rv reflect.Value) error { } func (d *decoder) errorTemplate(code byte, k reflect.Kind) error { - return fmt.Errorf("msgpack : invalid code %x decoding %v, %w", code, k, def.ErrCanNotDecode) + return fmt.Errorf("%w %x decoding as %v", def.ErrCanNotDecode, code, k) } diff --git a/internal/stream/decoding/decoding_test.go b/internal/stream/decoding/decoding_test.go index 2c3ae22..f079af7 100644 --- a/internal/stream/decoding/decoding_test.go +++ b/internal/stream/decoding/decoding_test.go @@ -1,7 +1,6 @@ package decoding import ( - "fmt" "io" "reflect" "testing" @@ -19,7 +18,6 @@ type AsXXXTestCase[T any] struct { ReadCount int Expected T Error error - IsTemplateError bool MethodAs func(d *decoder) func(reflect.Kind) (T, error) MethodAsWithCode func(d *decoder) func(byte, reflect.Kind) (T, error) MethodAsCustom func(d *decoder) (T, error) @@ -69,10 +67,6 @@ func (tc *AsXXXTestCase[T]) Run(t *testing.T) { tu.IsError(t, err, tc.Error) return } - if tc.IsTemplateError { - tu.ErrorContains(t, err, fmt.Sprintf("msgpack : invalid code %x", tc.Code)) - return - } tu.NoError(t, err) tu.Equal(t, v, tc.Expected) diff --git a/internal/stream/decoding/ext_test.go b/internal/stream/decoding/ext_test.go index 3f1ff17..7f206d4 100644 --- a/internal/stream/decoding/ext_test.go +++ b/internal/stream/decoding/ext_test.go @@ -5,9 +5,7 @@ import ( "testing" "github.com/shamaton/msgpack/v2/def" - "github.com/shamaton/msgpack/v2/internal/common" - tu "github.com/shamaton/msgpack/v2/internal/common/testutil" "github.com/shamaton/msgpack/v2/time" ) diff --git a/internal/stream/decoding/float_test.go b/internal/stream/decoding/float_test.go index eecba05..d4b59ea 100644 --- a/internal/stream/decoding/float_test.go +++ b/internal/stream/decoding/float_test.go @@ -1,10 +1,11 @@ package decoding import ( - "github.com/shamaton/msgpack/v2/def" "io" "reflect" "testing" + + "github.com/shamaton/msgpack/v2/def" ) func Test_asFloat32(t *testing.T) { @@ -88,7 +89,7 @@ func Test_asFloat32WithCode(t *testing.T) { { Name: "Unexpected", Code: def.Str8, - IsTemplateError: true, + Error: def.ErrCanNotDecode, MethodAsWithCode: method, }, } @@ -193,7 +194,7 @@ func Test_asFloat64WithCode(t *testing.T) { { Name: "Unexpected", Code: def.Str8, - IsTemplateError: true, + Error: def.ErrCanNotDecode, MethodAsWithCode: method, }, } diff --git a/internal/stream/decoding/interface_test.go b/internal/stream/decoding/interface_test.go index ed4d8bf..6f0b826 100644 --- a/internal/stream/decoding/interface_test.go +++ b/internal/stream/decoding/interface_test.go @@ -206,7 +206,7 @@ func Test_asInterfaceWithCode(t *testing.T) { Code: def.Fixext1, Data: []byte{4, 0}, ReadCount: 2, - IsTemplateError: true, + Error: def.ErrCanNotDecode, MethodAsWithCode: method, }, } diff --git a/internal/stream/decoding/map_test.go b/internal/stream/decoding/map_test.go index 5254d2c..77e660e 100644 --- a/internal/stream/decoding/map_test.go +++ b/internal/stream/decoding/map_test.go @@ -57,7 +57,7 @@ func Test_mapLength(t *testing.T) { { Name: "Unexpected", Code: def.Nil, - IsTemplateError: true, + Error: def.ErrCanNotDecode, MethodAsWithCode: method, }, } @@ -77,22 +77,22 @@ func Test_asFixedMap_StringInt(t *testing.T) { name := fmt.Sprintf("%T", v) testcases := AsXXXTestCases[bool]{ { - Name: name + ".error.asString", - Code: def.Int32, - Data: []byte{def.Int32}, - Expected: false, - ReadCount: 1, - MethodAsCustom: method, - IsTemplateError: true, + Name: name + ".error.asString", + Code: def.Int32, + Data: []byte{def.Int32}, + Expected: false, + ReadCount: 1, + Error: def.ErrCanNotDecode, + MethodAsCustom: method, }, { - Name: name + ".error.asInt", - Code: def.Str8, - Data: []byte{def.FixStr + 1, 'a', def.Str8}, - Expected: false, - ReadCount: 3, - MethodAsCustom: method, - IsTemplateError: true, + Name: name + ".error.asInt", + Code: def.Str8, + Data: []byte{def.FixStr + 1, 'a', def.Str8}, + Expected: false, + ReadCount: 3, + Error: def.ErrCanNotDecode, + MethodAsCustom: method, }, { Name: name + ".ok", @@ -134,22 +134,22 @@ func Test_asFixedMap_StringUint(t *testing.T) { name := fmt.Sprintf("%T", v) testcases := AsXXXTestCases[bool]{ { - Name: name + ".error.asString", - Code: def.Int32, - Data: []byte{def.Int32}, - Expected: false, - ReadCount: 1, - MethodAsCustom: method, - IsTemplateError: true, + Name: name + ".error.asString", + Code: def.Int32, + Data: []byte{def.Int32}, + Expected: false, + ReadCount: 1, + MethodAsCustom: method, + Error: def.ErrCanNotDecode, }, { - Name: name + ".error.asUint", - Code: def.Str8, - Data: []byte{def.FixStr + 1, 'a', def.Str8}, - Expected: false, - ReadCount: 3, - MethodAsCustom: method, - IsTemplateError: true, + Name: name + ".error.asUint", + Code: def.Str8, + Data: []byte{def.FixStr + 1, 'a', def.Str8}, + Expected: false, + ReadCount: 3, + MethodAsCustom: method, + Error: def.ErrCanNotDecode, }, { Name: name + ".ok", @@ -191,22 +191,22 @@ func Test_asFixedMap_StringFloat(t *testing.T) { name := fmt.Sprintf("%T", v) testcases := AsXXXTestCases[bool]{ { - Name: name + ".error.asString", - Code: def.Int32, - Data: []byte{def.Int32}, - Expected: false, - ReadCount: 1, - MethodAsCustom: method, - IsTemplateError: true, + Name: name + ".error.asString", + Code: def.Int32, + Data: []byte{def.Int32}, + Expected: false, + ReadCount: 1, + MethodAsCustom: method, + Error: def.ErrCanNotDecode, }, { - Name: name + ".error.asFloat", - Code: def.Str8, - Data: []byte{def.FixStr + 1, 'a', def.Str8}, - Expected: false, - ReadCount: 3, - MethodAsCustom: method, - IsTemplateError: true, + Name: name + ".error.asFloat", + Code: def.Str8, + Data: []byte{def.FixStr + 1, 'a', def.Str8}, + Expected: false, + ReadCount: 3, + MethodAsCustom: method, + Error: def.ErrCanNotDecode, }, { Name: name + ".ok", @@ -239,22 +239,22 @@ func Test_asFixedMap_StringBool(t *testing.T) { name := fmt.Sprintf("%T", v) testcases := AsXXXTestCases[bool]{ { - Name: name + ".error.asString", - Code: def.Int32, - Data: []byte{def.Int32}, - Expected: false, - ReadCount: 1, - MethodAsCustom: method, - IsTemplateError: true, + Name: name + ".error.asString", + Code: def.Int32, + Data: []byte{def.Int32}, + Expected: false, + ReadCount: 1, + Error: def.ErrCanNotDecode, + MethodAsCustom: method, }, { - Name: name + ".error.asBool", - Code: def.Str8, - Data: []byte{def.FixStr + 1, 'a', def.Str8}, - Expected: false, - ReadCount: 3, - MethodAsCustom: method, - IsTemplateError: true, + Name: name + ".error.asBool", + Code: def.Str8, + Data: []byte{def.FixStr + 1, 'a', def.Str8}, + Expected: false, + ReadCount: 3, + Error: def.ErrCanNotDecode, + MethodAsCustom: method, }, { Name: name + ".ok", @@ -284,22 +284,22 @@ func Test_asFixedMap_StringString(t *testing.T) { name := fmt.Sprintf("%T", v) testcases := AsXXXTestCases[bool]{ { - Name: name + ".error.asString", - Code: def.Int32, - Data: []byte{def.Int32}, - Expected: false, - ReadCount: 1, - MethodAsCustom: method, - IsTemplateError: true, + Name: name + ".error.asString", + Code: def.Int32, + Data: []byte{def.Int32}, + Expected: false, + ReadCount: 1, + Error: def.ErrCanNotDecode, + MethodAsCustom: method, }, { - Name: name + ".error.asString", - Code: def.Int32, - Data: []byte{def.FixStr + 1, 'a', def.Int32}, - Expected: false, - ReadCount: 3, - MethodAsCustom: method, - IsTemplateError: true, + Name: name + ".error.asString", + Code: def.Int32, + Data: []byte{def.FixStr + 1, 'a', def.Int32}, + Expected: false, + ReadCount: 3, + Error: def.ErrCanNotDecode, + MethodAsCustom: method, }, { Name: name + ".ok", @@ -329,22 +329,22 @@ func Test_asFixedMap_IntString(t *testing.T) { name := fmt.Sprintf("%T", v) testcases := AsXXXTestCases[bool]{ { - Name: name + ".error.asInt", - Code: def.Str8, - Data: []byte{def.Str8}, - Expected: false, - ReadCount: 1, - MethodAsCustom: method, - IsTemplateError: true, + Name: name + ".error.asInt", + Code: def.Str8, + Data: []byte{def.Str8}, + Expected: false, + ReadCount: 1, + Error: def.ErrCanNotDecode, + MethodAsCustom: method, }, { - Name: name + ".error.asString", - Code: def.Int32, - Data: []byte{def.Int8, dv, def.Int32}, - Expected: false, - ReadCount: 3, - MethodAsCustom: method, - IsTemplateError: true, + Name: name + ".error.asString", + Code: def.Int32, + Data: []byte{def.Int8, dv, def.Int32}, + Expected: false, + ReadCount: 3, + Error: def.ErrCanNotDecode, + MethodAsCustom: method, }, { Name: name + ".ok", @@ -386,22 +386,22 @@ func Test_asFixedMap_IntBool(t *testing.T) { name := fmt.Sprintf("%T", v) testcases := AsXXXTestCases[bool]{ { - Name: name + ".error.asInt", - Code: def.Str8, - Data: []byte{def.Str8}, - Expected: false, - ReadCount: 1, - MethodAsCustom: method, - IsTemplateError: true, + Name: name + ".error.asInt", + Code: def.Str8, + Data: []byte{def.Str8}, + Expected: false, + ReadCount: 1, + Error: def.ErrCanNotDecode, + MethodAsCustom: method, }, { - Name: name + ".error.asBool", - Code: def.Int32, - Data: []byte{def.Int8, dv, def.Int32}, - Expected: false, - ReadCount: 3, - MethodAsCustom: method, - IsTemplateError: true, + Name: name + ".error.asBool", + Code: def.Int32, + Data: []byte{def.Int8, dv, def.Int32}, + Expected: false, + ReadCount: 3, + Error: def.ErrCanNotDecode, + MethodAsCustom: method, }, { Name: name + ".ok", @@ -443,22 +443,22 @@ func Test_asFixedMap_UintString(t *testing.T) { name := fmt.Sprintf("%T", v) testcases := AsXXXTestCases[bool]{ { - Name: name + ".error.asUint", - Code: def.Str8, - Data: []byte{def.Str8}, - Expected: false, - ReadCount: 1, - MethodAsCustom: method, - IsTemplateError: true, + Name: name + ".error.asUint", + Code: def.Str8, + Data: []byte{def.Str8}, + Expected: false, + ReadCount: 1, + Error: def.ErrCanNotDecode, + MethodAsCustom: method, }, { - Name: name + ".error.asString", - Code: def.Int32, - Data: []byte{def.Uint8, dv, def.Int32}, - Expected: false, - MethodAsCustom: method, - ReadCount: 3, - IsTemplateError: true, + Name: name + ".error.asString", + Code: def.Int32, + Data: []byte{def.Uint8, dv, def.Int32}, + Expected: false, + ReadCount: 3, + Error: def.ErrCanNotDecode, + MethodAsCustom: method, }, { Name: name + ".ok", @@ -500,22 +500,22 @@ func Test_asFixedMap_UintBool(t *testing.T) { name := fmt.Sprintf("%T", v) testcases := AsXXXTestCases[bool]{ { - Name: name + ".error.asUint", - Code: def.Str8, - Data: []byte{def.Str8}, - Expected: false, - ReadCount: 1, - MethodAsCustom: method, - IsTemplateError: true, + Name: name + ".error.asUint", + Code: def.Str8, + Data: []byte{def.Str8}, + Expected: false, + ReadCount: 1, + Error: def.ErrCanNotDecode, + MethodAsCustom: method, }, { - Name: name + ".error.asBool", - Code: def.Int32, - Data: []byte{def.Uint8, dv, def.Int32}, - Expected: false, - ReadCount: 3, - MethodAsCustom: method, - IsTemplateError: true, + Name: name + ".error.asBool", + Code: def.Int32, + Data: []byte{def.Uint8, dv, def.Int32}, + Expected: false, + ReadCount: 3, + Error: def.ErrCanNotDecode, + MethodAsCustom: method, }, { Name: name + ".ok", @@ -557,22 +557,22 @@ func Test_asFixedMap_FloatString(t *testing.T) { name := fmt.Sprintf("%T", v) testcases := AsXXXTestCases[bool]{ { - Name: name + ".error.asFloat", - Code: def.Str8, - Data: []byte{def.Str8}, - Expected: false, - ReadCount: 1, - MethodAsCustom: method, - IsTemplateError: true, + Name: name + ".error.asFloat", + Code: def.Str8, + Data: []byte{def.Str8}, + Expected: false, + ReadCount: 1, + Error: def.ErrCanNotDecode, + MethodAsCustom: method, }, { - Name: name + ".error.asString", - Code: def.Int32, - Data: []byte{def.Uint8, dv, def.Int32}, - Expected: false, - ReadCount: 3, - MethodAsCustom: method, - IsTemplateError: true, + Name: name + ".error.asString", + Code: def.Int32, + Data: []byte{def.Uint8, dv, def.Int32}, + Expected: false, + ReadCount: 3, + Error: def.ErrCanNotDecode, + MethodAsCustom: method, }, { Name: name + ".ok", @@ -610,9 +610,8 @@ func Test_asFixedMap_FloatBool(t *testing.T) { Data: []byte{def.Str8}, Expected: false, ReadCount: 1, + Error: def.ErrCanNotDecode, MethodAsCustom: method, - - IsTemplateError: true, }, { Name: name + ".error.asBool", @@ -620,9 +619,8 @@ func Test_asFixedMap_FloatBool(t *testing.T) { Data: []byte{def.Uint8, dv, def.Int32}, Expected: false, ReadCount: 3, + Error: def.ErrCanNotDecode, MethodAsCustom: method, - - IsTemplateError: true, }, { Name: name + ".ok", diff --git a/msgpack_test.go b/msgpack_test.go index b409b53..6ef5fb1 100644 --- a/msgpack_test.go +++ b/msgpack_test.go @@ -14,11 +14,10 @@ import ( "testing" "time" - "github.com/shamaton/msgpack/v2/internal/common" - "github.com/shamaton/msgpack/v2" "github.com/shamaton/msgpack/v2/def" "github.com/shamaton/msgpack/v2/ext" + "github.com/shamaton/msgpack/v2/internal/common" extTime "github.com/shamaton/msgpack/v2/time" ) From aaf2f863b8ab8e8d4064b9ed1c71705a97cee23c Mon Sep 17 00:00:00 2001 From: shamaton Date: Tue, 27 Aug 2024 00:13:52 +0900 Subject: [PATCH 41/41] fix gosimple, govet --- internal/decoding/decoding_test.go | 3 +-- internal/stream/decoding/decoding_test.go | 3 +-- internal/stream/encoding/struct_test.go | 4 ++-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/internal/decoding/decoding_test.go b/internal/decoding/decoding_test.go index 3c78427..1b8d11f 100644 --- a/internal/decoding/decoding_test.go +++ b/internal/decoding/decoding_test.go @@ -714,8 +714,7 @@ func Test_decodeWithCode(t *testing.T) { MethodAs: method, }, } - var v interface{} - v = new(int) + v := (any)(new(int)) target = &v testcases.Run(t) vv := v.(*int) diff --git a/internal/stream/decoding/decoding_test.go b/internal/stream/decoding/decoding_test.go index f079af7..fc10c34 100644 --- a/internal/stream/decoding/decoding_test.go +++ b/internal/stream/decoding/decoding_test.go @@ -823,8 +823,7 @@ func Test_decodeWithCode(t *testing.T) { MethodAsWithCode: method, }, } - var v interface{} - v = new(int) + v := (any)(new(int)) target = &v testcases.Run(t) vv := v.(*int) diff --git a/internal/stream/encoding/struct_test.go b/internal/stream/encoding/struct_test.go index 25de8e2..93bce50 100644 --- a/internal/stream/encoding/struct_test.go +++ b/internal/stream/encoding/struct_test.go @@ -28,11 +28,11 @@ func Test_writeStruct(t *testing.T) { { Name: "ok", Value: value, - Expected: append([]byte{ + Expected: []byte{ def.Ext8, 12, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xf1, 0x88, 0x6e, 0x09, 0x00, - }), + }, BufferSize: 1, MethodForStruct: method, },