Skip to content

Commit

Permalink
Merge pull request #39 from shamaton/add_internal_tests
Browse files Browse the repository at this point in the history
Add internal tests and fix some bugs
  • Loading branch information
shamaton authored Aug 26, 2024
2 parents d1c8383 + aaf2f86 commit ead6a6b
Show file tree
Hide file tree
Showing 70 changed files with 12,268 additions and 74 deletions.
8 changes: 7 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
31 changes: 31 additions & 0 deletions def/error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package def

import (
"errors"
"fmt"
)

var (
// 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 = 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)
ErrUnsupportedType = fmt.Errorf("%wunsupported type", ErrMsgpack)
ErrUnsupportedLength = fmt.Errorf("%wunsupported length", ErrMsgpack)
ErrNotMatchLastIndex = fmt.Errorf("%wnot match last index", ErrMsgpack)
)
8 changes: 8 additions & 0 deletions errors.go
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion internal/common/buffer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
48 changes: 48 additions & 0 deletions internal/common/common_test.go
Original file line number Diff line number Diff line change
@@ -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, "")
})
}
42 changes: 42 additions & 0 deletions internal/common/testutil/reader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package testutil

import (
"errors"
"io"
)

var ErrReaderErr = errors.New("reader error")

type ErrReader struct{}

func NewErrReader() *ErrReader {
return &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
}
56 changes: 56 additions & 0 deletions internal/common/testutil/struct.go
Original file line number Diff line number Diff line change
@@ -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
}
93 changes: 93 additions & 0 deletions internal/common/testutil/testutil.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package testutil

import (
"errors"
"reflect"
"strings"
"testing"
)

func NoError(t *testing.T, err error) {
t.Helper()
if err != nil {
t.Fatalf("error is not nil: %v", 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 any](t *testing.T, actual, expected T) {
t.Helper()
if !reflect.DeepEqual(actual, expected) {
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) {
switch a := any(actual).(type) {
case []byte:
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 a := any(actual).(type) {
case []byte:
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)
}
}
}
}

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)
}
for k, v1 := range actual {
if v2, ok := expected[k]; !ok || v1 != v2 {
t.Fatalf("not equal. actual: %v, expected: %v", actual, expected)
}
}
}

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)
}
}
21 changes: 15 additions & 6 deletions internal/decoding/bin.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading

0 comments on commit ead6a6b

Please sign in to comment.