gojay

high performance JSON encoder/decoder with stream API for Golang
git clone git://git.lair.cx/gojay
Log | Files | Refs | README | LICENSE

commit 7fd98627fec0688476bdf9793fb945509a03ab4f
parent 45dc1eee0a94589d293653fda93ff5f4041a7c00
Author: francoispqt <francois@parquet.ninja>
Date:   Sun, 29 Apr 2018 00:01:37 +0800

add edge case tests

Diffstat:
Mdecode_array.go | 4++--
Mdecode_array_test.go | 35+++++++++++++++++++++++++++++++++++
Mdecode_number.go | 2+-
Mdecode_number_test.go | 35++++++++++++++++++++++++++++-------
Mdecode_object_test.go | 9+++++++--
Mencode.go | 7+++++++
Aencode_builder_test.go | 17+++++++++++++++++
Mencode_interface.go | 28++++++++++++++--------------
Mencode_interface_test.go | 14++++++++++++++
Mencode_object_test.go | 4++--
10 files changed, 127 insertions(+), 28 deletions(-)

diff --git a/decode_array.go b/decode_array.go @@ -71,7 +71,7 @@ func (dec *Decoder) skipArray() (int, error) { var arraysOpen = 1 var arraysClosed = 0 // var stringOpen byte = 0 - for j := dec.cursor; j < dec.length; j++ { + for j := dec.cursor; j < dec.length || dec.read(); j++ { switch dec.data[j] { case ']': arraysClosed++ @@ -94,7 +94,7 @@ func (dec *Decoder) skipArray() (int, error) { // loop backward and count how many anti slash found // to see if string is effectively escaped ct := 1 - for i := j; i > 0; i-- { + for i := j - 2; i > 0; i-- { if dec.data[i] != '\\' { break } diff --git a/decode_array_test.go b/decode_array_test.go @@ -189,3 +189,38 @@ func TestDecoderSliceDecoderAPIError(t *testing.T) { assert.NotNil(t, err, "Err must not be nil as JSON is invalid") assert.IsType(t, InvalidJSONError(""), err, "err message must be 'Invalid JSON'") } + +func TestSkipArray(t *testing.T) { + testCases := []struct { + json string + expectations func(*testing.T, int, error) + }{ + { + json: `"testbasic"]`, + expectations: func(t *testing.T, i int, err error) { + assert.Equal(t, len(`"testbasic"]`), i) + assert.Nil(t, err) + }, + }, + { + json: `"test \\\\\" escape"]`, + expectations: func(t *testing.T, i int, err error) { + assert.Equal(t, len(`"test \\\\\" escape"]`), i) + assert.Nil(t, err) + }, + }, + { + json: `"test \\\\\\"]`, + expectations: func(t *testing.T, i int, err error) { + assert.Equal(t, len(`"test \\\\\\"]`), i) + assert.Nil(t, err) + }, + }, + } + + for _, test := range testCases { + dec := NewDecoder(strings.NewReader(test.json)) + i, err := dec.skipArray() + test.expectations(t, i, err) + } +} diff --git a/decode_number.go b/decode_number.go @@ -581,7 +581,7 @@ func (dec *Decoder) atoi32(start, end int) int32 { for i := start + 1; i < end; i++ { intv := int32(digits[dec.data[i]]) if val > maxInt32toMultiply { - dec.err = InvalidTypeError("Overflows int321") + dec.err = InvalidTypeError("Overflows int32") return 0 } val = (val << 3) + (val << 1) diff --git a/decode_number_test.go b/decode_number_test.go @@ -63,6 +63,20 @@ func TestDecoderIntOverfow(t *testing.T) { assert.NotNil(t, err, "Err must not be nil as int is overflowing") assert.Equal(t, 0, v, "v must be equal to 0") } +func TestDecoderIntOverfow2(t *testing.T) { + json := []byte(`92233720368547758089 `) + var v int + err := Unmarshal(json, &v) + assert.NotNil(t, err, "Err must not be nil as int is overflowing") + assert.Equal(t, 0, v, "v must be equal to 0") +} +func TestDecoderIntOverfow3(t *testing.T) { + json := []byte(`92233720368547758089 `) + var v int + err := Unmarshal(json, &v) + assert.NotNil(t, err, "Err must not be nil as int is overflowing") + assert.Equal(t, 0, v, "v must be equal to 0") +} func TestDecoderIntPoolError(t *testing.T) { result := int(1) dec := NewDecoder(nil) @@ -75,13 +89,6 @@ func TestDecoderIntPoolError(t *testing.T) { _ = dec.DecodeInt(&result) assert.True(t, false, "should not be called as decoder should have panicked") } -func TestDecoderIntOverfow2(t *testing.T) { - json := []byte(`92233720368547758089 `) - var v int - err := Unmarshal(json, &v) - assert.NotNil(t, err, "Err must not be nil as int is overflowing") - assert.Equal(t, 0, v, "v must be equal to 0") -} func TestDecoderInttDecoderAPI(t *testing.T) { var v int dec := NewDecoder(strings.NewReader(`33`)) @@ -398,6 +405,20 @@ func TestDecoderFloatBasic(t *testing.T) { assert.Nil(t, err, "Err must be nil") assert.Equal(t, 100.11, v, "v must be equal to 100.11") } +func TestDecoderFloatBasic2(t *testing.T) { + json := []byte(` 100.11 `) + var v float64 + err := Unmarshal(json, &v) + assert.Nil(t, err, "Err must be nil") + assert.Equal(t, 100.11, v, "v must be equal to 100.11") +} +func TestDecoderFloatBasic3(t *testing.T) { + json := []byte(` 100 `) + var v float64 + err := Unmarshal(json, &v) + assert.Nil(t, err, "Err must be nil") + assert.Equal(t, float64(100), v, "v must be equal to 100.11") +} func TestDecoderFloatBig(t *testing.T) { json := []byte(`89899843.3493493 `) diff --git a/decode_object_test.go b/decode_object_test.go @@ -154,8 +154,13 @@ func TestDecodeObjectNull(t *testing.T) { var jsonComplex = []byte(`{ "test": "{\"test\":\"1\",\"test1\":2}", "test2\\n": "\\\\\\\\\\\n", - "testArrSkip": ["testString with escaped \" quotes"], + "testArrSkip": ["testString with escaped \\\" quotes"], "testSkipString": "skip \\ string with \\n escaped char \" ", + "testSkipObject": { + "testSkipSubObj": { + "test": "test" + } + }, "testSkipNumber": 123.23, "testBool": true, "testSub": { @@ -210,7 +215,7 @@ func TestDecodeObjComplex(t *testing.T) { result := jsonObjectComplex{} err := UnmarshalObject(jsonComplex, &result) assert.NotNil(t, err, "err should not be as invalid type as been encountered nil") - assert.Equal(t, `Cannot unmarshal to struct, wrong char '"' found at pos 460`, err.Error(), "err should not be as invalid type as been encountered nil") + assert.Equal(t, `Cannot unmarshal to struct, wrong char '"' found at pos 531`, err.Error(), "err should not be as invalid type as been encountered nil") assert.Equal(t, `{"test":"1","test1":2}`, result.Test, "result.Test is not expected value") assert.Equal(t, `\\\\\\n`, result.Test2, "result.Test2 is not expected value") assert.Equal(t, 1, result.Test3, "result.test3 is not expected value") diff --git a/encode.go b/encode.go @@ -1,5 +1,10 @@ package gojay +import ( + "fmt" + "reflect" +) + // MarshalObject returns the JSON encoding of v. // // It takes a struct implementing Marshaler to a JSON slice of byte @@ -162,6 +167,8 @@ func Marshal(v interface{}) ([]byte, error) { enc := BorrowEncoder() defer enc.Release() return enc.encodeFloat(float64(vt)) + default: + err = InvalidMarshalError(fmt.Sprintf(invalidMarshalErrorMsg, reflect.TypeOf(vt).String())) } return b, err } diff --git a/encode_builder_test.go b/encode_builder_test.go @@ -0,0 +1,16 @@ +package gojay + +import ( + "testing" + "github.com/stretchr/testify/assert" +) + +func TestEncoderBuilderError(t *testing.T) { + enc := NewEncoder() + defer func() { + err := recover() + assert.NotNil(t, err, "err is not nil as we pass an invalid number to grow") + }() + enc.grow(-1) + assert.True(t, false, "should not be called") +} +\ No newline at end of file diff --git a/encode_interface.go b/encode_interface.go @@ -85,37 +85,37 @@ func (enc *Encoder) AddInterface(value interface{}) error { // AddInterfaceKey adds an interface{} to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) AddInterfaceKey(key string, value interface{}) error { - switch value.(type) { + switch vt := value.(type) { case string: - return enc.AddStringKey(key, value.(string)) + return enc.AddStringKey(key, vt) case bool: - return enc.AddBoolKey(key, value.(bool)) + return enc.AddBoolKey(key, vt) case MarshalerArray: return enc.AddArrayKey(key, value.(MarshalerArray)) case MarshalerObject: return enc.AddObjectKey(key, value.(MarshalerObject)) case int: - return enc.AddIntKey(key, value.(int)) + return enc.AddIntKey(key, vt) case int64: - return enc.AddIntKey(key, int(value.(int64))) + return enc.AddIntKey(key, int(vt)) case int32: - return enc.AddIntKey(key, int(value.(int32))) + return enc.AddIntKey(key, int(vt)) case int16: - return enc.AddIntKey(key, int(value.(int16))) + return enc.AddIntKey(key, int(vt)) case int8: - return enc.AddIntKey(key, int(value.(int8))) + return enc.AddIntKey(key, int(vt)) case uint64: - return enc.AddIntKey(key, int(value.(uint64))) + return enc.AddIntKey(key, int(vt)) case uint32: - return enc.AddIntKey(key, int(value.(uint32))) + return enc.AddIntKey(key, int(vt)) case uint16: - return enc.AddIntKey(key, int(value.(uint16))) + return enc.AddIntKey(key, int(vt)) case uint8: - return enc.AddIntKey(key, int(value.(uint8))) + return enc.AddIntKey(key, int(vt)) case float64: - return enc.AddFloatKey(key, value.(float64)) + return enc.AddFloatKey(key, vt) case float32: - return enc.AddFloat32Key(key, value.(float32)) + return enc.AddFloat32Key(key, vt) } return nil diff --git a/encode_interface_test.go b/encode_interface_test.go @@ -53,6 +53,13 @@ var encoderTestCases = []struct { }, }, { + v: uint16(100), + expectations: func(t *testing.T, b []byte, err error) { + assert.Nil(t, err, "err should be nil") + assert.Equal(t, "100", string(b), "string(b) should equal 100") + }, + }, + { v: uint8(100), expectations: func(t *testing.T, b []byte, err error) { assert.Nil(t, err, "err should be nil") @@ -101,6 +108,13 @@ var encoderTestCases = []struct { assert.Equal(t, `{"testStr":"漢字","testInt":1,"testInt64":1,"testInt32":1,"testInt16":1,"testInt8":1,"testUint64":1,"testUint32":1,"testUint16":1,"testUint8":1,"testFloat64":1.1,"testFloat32":1.1,"testBool":true}`, string(b), `string(b) should equal {"testStr":"漢字","testInt":1,"testInt64":1,"testInt32":1,"testInt16":1,"testInt8":1,"testUint64":1,"testUint32":1,"testUint16":1,"testUint8":1,"testFloat64":1.1,"testFloat32":1.1,"testBool":true}`) }, }, + { + v: &struct{}{}, + expectations: func(t *testing.T, b []byte, err error) { + assert.NotNil(t, err, "err should be nil") + assert.IsType(t, InvalidMarshalError(""), err, "err should be of type InvalidMarshalError") + }, + }, } func TestEncoderInterfaceAllTypesDecoderAPI(t *testing.T) { diff --git a/encode_object_test.go b/encode_object_test.go @@ -74,11 +74,11 @@ func (t *TestEncoding) MarshalObject(enc *Encoder) { enc.AddStringKey("test2", t.test2) enc.AddIntKey("testInt", t.testInt) enc.AddBoolKey("testBool", t.testBool) - enc.AddArrayKey("testArr", t.testArr) + enc.AddInterfaceKey("testArr", t.testArr) enc.AddInterfaceKey("testF64", t.testF64) enc.AddInterfaceKey("testF32", t.testF32) enc.AddInterfaceKey("testInterface", t.testInterface) - enc.AddObjectKey("sub", t.sub) + enc.AddInterfaceKey("sub", t.sub) } type SubObject struct {