json: Marshal, Unmarshal using new scanner

R=r
CC=golang-dev
https://golang.org/cl/953041
This commit is contained in:
Russ Cox 2010-04-21 16:40:53 -07:00
parent 214a55b06a
commit dba9d62bc2
11 changed files with 1570 additions and 1053 deletions

View file

@ -1,133 +1,427 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package json
import (
"container/vector"
"bytes"
"reflect"
"strings"
"testing"
)
func TestDecodeInt64(t *testing.T) {
nb := newDecoder(nil, nil)
nb.Int64(-15)
assertResult(t, nb.Data(), float64(-15))
type unmarshalTest struct {
in string
ptr interface{}
out interface{}
}
func TestDecodeUint64(t *testing.T) {
nb := newDecoder(nil, nil)
nb.Uint64(15)
assertResult(t, nb.Data(), float64(15))
var unmarshalTests = []unmarshalTest{
// basic types
unmarshalTest{`true`, new(bool), true},
unmarshalTest{`1`, new(int), 1},
unmarshalTest{`1.2`, new(float), 1.2},
unmarshalTest{`-5`, new(int16), int16(-5)},
unmarshalTest{`"a\u1234"`, new(string), "a\u1234"},
unmarshalTest{`"g-clef: \uD834\uDD1E"`, new(string), "g-clef: \U0001D11E"},
unmarshalTest{`"invalid: \uD834x\uDD1E"`, new(string), "invalid: \uFFFDx\uFFFD"},
unmarshalTest{"null", new(interface{}), nil},
// composite tests
unmarshalTest{allValueIndent, new(All), allValue},
unmarshalTest{allValueCompact, new(All), allValue},
unmarshalTest{allValueIndent, new(*All), &allValue},
unmarshalTest{allValueCompact, new(*All), &allValue},
unmarshalTest{pallValueIndent, new(All), pallValue},
unmarshalTest{pallValueCompact, new(All), pallValue},
unmarshalTest{pallValueIndent, new(*All), &pallValue},
unmarshalTest{pallValueCompact, new(*All), &pallValue},
}
func TestDecodeFloat64(t *testing.T) {
nb := newDecoder(nil, nil)
nb.Float64(3.14159)
assertResult(t, nb.Data(), float64(3.14159))
}
func TestMarshal(t *testing.T) {
b, err := Marshal(allValue)
if err != nil {
t.Fatalf("Marshal allValue: %v", err)
}
if string(b) != allValueCompact {
t.Errorf("Marshal allValueCompact")
diff(t, b, []byte(allValueCompact))
return
}
func TestDecodeString(t *testing.T) {
nb := newDecoder(nil, nil)
nb.String("Some string")
assertResult(t, nb.Data(), "Some string")
}
func TestDecodeBool(t *testing.T) {
nb := newDecoder(nil, nil)
nb.Bool(true)
assertResult(t, nb.Data(), true)
}
func TestDecodeNull(t *testing.T) {
nb := newDecoder(nil, nil)
nb.Null()
assertResult(t, nb.Data(), nil)
}
func TestDecodeEmptyArray(t *testing.T) {
nb := newDecoder(nil, nil)
nb.Array()
assertResult(t, nb.Data(), []interface{}{})
}
func TestDecodeEmptyMap(t *testing.T) {
nb := newDecoder(nil, nil)
nb.Map()
assertResult(t, nb.Data(), map[string]interface{}{})
}
func TestDecodeFlushElem(t *testing.T) {
testVec := new(vector.Vector).Resize(2, 2)
nb := newDecoder(testVec, 1)
nb.Float64(3.14159)
nb.Flush()
assertResult(t, testVec.Data(), []interface{}{nil, float64(3.14159)})
}
func TestDecodeFlushKey(t *testing.T) {
testMap := make(map[string]interface{})
nb := newDecoder(testMap, "key")
nb.Float64(3.14159)
nb.Flush()
assertResult(t, testMap, map[string]interface{}{"key": float64(3.14159)})
}
// Elem() and Key() are hard to test in isolation because all they do
// is create a new, properly initialized, decoder, and modify state of
// the underlying decoder. I'm testing them through already tested
// Array(), String(), and Flush().
func TestDecodeElem(t *testing.T) {
nb := newDecoder(nil, nil)
nb.Array()
var b Builder = nb.Elem(0)
b.String("0")
b.Flush()
assertResult(t, nb.Data(), []interface{}{"0"})
}
func TestDecodeKey(t *testing.T) {
nb := newDecoder(nil, nil)
nb.Map()
var b Builder = nb.Key("a")
b.String("0")
b.Flush()
assertResult(t, nb.Data(), map[string]interface{}{"a": "0"})
}
func assertResult(t *testing.T, results, expected interface{}) {
if !reflect.DeepEqual(results, expected) {
t.Fatalf("have %T(%#v) want %T(%#v)", results, results, expected, expected)
b, err = Marshal(pallValue)
if err != nil {
t.Fatalf("Marshal pallValue: %v", err)
}
if string(b) != pallValueCompact {
t.Errorf("Marshal pallValueCompact")
diff(t, b, []byte(pallValueCompact))
return
}
}
type decodeTest struct {
s string
r interface{}
}
var tests = []decodeTest{
decodeTest{`null`, nil},
decodeTest{`true`, true},
decodeTest{`false`, false},
decodeTest{`"abc"`, "abc"},
decodeTest{`123`, float64(123)},
decodeTest{`0.1`, float64(0.1)},
decodeTest{`1e-10`, float64(1e-10)},
decodeTest{`[]`, []interface{}{}},
decodeTest{`[1,2,3,4]`, []interface{}{float64(1), float64(2), float64(3), float64(4)}},
decodeTest{`[1,2,"abc",null,true,false]`, []interface{}{float64(1), float64(2), "abc", nil, true, false}},
decodeTest{`{}`, map[string]interface{}{}},
decodeTest{`{"a":1}`, map[string]interface{}{"a": float64(1)}},
decodeTest{`"q\u0302"`, "q\u0302"},
}
func TestDecode(t *testing.T) {
for _, test := range tests {
if val, err := Decode(test.s); err != nil || !reflect.DeepEqual(val, test.r) {
t.Errorf("Decode(%#q) = %v, %v want %v, nil", test.s, val, err, test.r)
func TestUnmarshal(t *testing.T) {
var scan scanner
for i, tt := range unmarshalTests {
in := []byte(tt.in)
if err := checkValid(in, &scan); err != nil {
t.Errorf("#%d: checkValid: %v", i, err)
continue
}
// v = new(right-type)
v := reflect.NewValue(tt.ptr).(*reflect.PtrValue)
v.PointTo(reflect.MakeZero(v.Type().(*reflect.PtrType).Elem()))
if err := Unmarshal([]byte(in), v.Interface()); err != nil {
t.Errorf("#%d: %v", i, err)
continue
}
if !reflect.DeepEqual(v.Elem().Interface(), tt.out) {
t.Errorf("#%d: mismatch\nhave: %#+v\nwant: %#+v", i, v.Elem().Interface(), tt.out)
data, _ := Marshal(v.Elem().Interface())
println(string(data))
data, _ = Marshal(tt.out)
println(string(data))
return
continue
}
}
}
func TestUnmarshalMarshal(t *testing.T) {
var v interface{}
if err := Unmarshal(jsonBig, &v); err != nil {
t.Fatalf("Unmarshal: %v", err)
}
b, err := Marshal(v)
if err != nil {
t.Fatalf("Marshal: %v", err)
}
if bytes.Compare(jsonBig, b) != 0 {
t.Errorf("Marshal jsonBig")
diff(t, b, jsonBig)
return
}
}
func noSpace(c int) int {
if isSpace(c) {
return -1
}
return c
}
type All struct {
Bool bool
Int int
Int8 int8
Int16 int16
Int32 int32
Int64 int64
Uint uint
Uint8 uint8
Uint16 uint16
Uint32 uint32
Uint64 uint64
Uintptr uintptr
Float float
Float32 float32
Float64 float64
Foo string "bar"
PBool *bool
PInt *int
PInt8 *int8
PInt16 *int16
PInt32 *int32
PInt64 *int64
PUint *uint
PUint8 *uint8
PUint16 *uint16
PUint32 *uint32
PUint64 *uint64
PUintptr *uintptr
PFloat *float
PFloat32 *float32
PFloat64 *float64
String string
PString *string
Map map[string]Small
MapP map[string]*Small
PMap *map[string]Small
PMapP *map[string]*Small
EmptyMap map[string]Small
NilMap map[string]Small
Slice []Small
SliceP []*Small
PSlice *[]Small
PSliceP *[]*Small
EmptySlice []Small
NilSlice []Small
StringSlice []string
ByteSlice []byte
Small Small
PSmall *Small
PPSmall **Small
Interface interface{}
PInterface *interface{}
}
type Small struct {
Tag string
}
var allValue = All{
Bool: true,
Int: 2,
Int8: 3,
Int16: 4,
Int32: 5,
Int64: 6,
Uint: 7,
Uint8: 8,
Uint16: 9,
Uint32: 10,
Uint64: 11,
Uintptr: 12,
Float: 13.1,
Float32: 14.1,
Float64: 15.1,
Foo: "foo",
String: "16",
Map: map[string]Small{
"17": Small{Tag: "tag17"},
"18": Small{Tag: "tag18"},
},
MapP: map[string]*Small{
"19": &Small{Tag: "tag19"},
"20": nil,
},
EmptyMap: map[string]Small{},
Slice: []Small{Small{Tag: "tag20"}, Small{Tag: "tag21"}},
SliceP: []*Small{&Small{Tag: "tag22"}, nil, &Small{Tag: "tag23"}},
EmptySlice: []Small{},
StringSlice: []string{"str24", "str25", "str26"},
ByteSlice: []byte{27, 28, 29},
Small: Small{Tag: "tag30"},
PSmall: &Small{Tag: "tag31"},
Interface: float64(5.2),
}
var pallValue = All{
PBool: &allValue.Bool,
PInt: &allValue.Int,
PInt8: &allValue.Int8,
PInt16: &allValue.Int16,
PInt32: &allValue.Int32,
PInt64: &allValue.Int64,
PUint: &allValue.Uint,
PUint8: &allValue.Uint8,
PUint16: &allValue.Uint16,
PUint32: &allValue.Uint32,
PUint64: &allValue.Uint64,
PUintptr: &allValue.Uintptr,
PFloat: &allValue.Float,
PFloat32: &allValue.Float32,
PFloat64: &allValue.Float64,
PString: &allValue.String,
PMap: &allValue.Map,
PMapP: &allValue.MapP,
PSlice: &allValue.Slice,
PSliceP: &allValue.SliceP,
PPSmall: &allValue.PSmall,
PInterface: &allValue.Interface,
}
var allValueIndent = `{
"bool": true,
"int": 2,
"int8": 3,
"int16": 4,
"int32": 5,
"int64": 6,
"uint": 7,
"uint8": 8,
"uint16": 9,
"uint32": 10,
"uint64": 11,
"uintptr": 12,
"float": 13.1,
"float32": 14.1,
"float64": 15.1,
"bar": "foo",
"pbool": null,
"pint": null,
"pint8": null,
"pint16": null,
"pint32": null,
"pint64": null,
"puint": null,
"puint8": null,
"puint16": null,
"puint32": null,
"puint64": null,
"puintptr": null,
"pfloat": null,
"pfloat32": null,
"pfloat64": null,
"string": "16",
"pstring": null,
"map": {
"17": {
"tag": "tag17"
},
"18": {
"tag": "tag18"
}
},
"mapp": {
"19": {
"tag": "tag19"
},
"20": null
},
"pmap": null,
"pmapp": null,
"emptymap": {},
"nilmap": null,
"slice": [
{
"tag": "tag20"
},
{
"tag": "tag21"
}
],
"slicep": [
{
"tag": "tag22"
},
null,
{
"tag": "tag23"
}
],
"pslice": null,
"pslicep": null,
"emptyslice": [],
"nilslice": [],
"stringslice": [
"str24",
"str25",
"str26"
],
"byteslice": [
27,
28,
29
],
"small": {
"tag": "tag30"
},
"psmall": {
"tag": "tag31"
},
"ppsmall": null,
"interface": 5.2,
"pinterface": null
}`
var allValueCompact = strings.Map(noSpace, allValueIndent)
var pallValueIndent = `{
"bool": false,
"int": 0,
"int8": 0,
"int16": 0,
"int32": 0,
"int64": 0,
"uint": 0,
"uint8": 0,
"uint16": 0,
"uint32": 0,
"uint64": 0,
"uintptr": 0,
"float": 0,
"float32": 0,
"float64": 0,
"bar": "",
"pbool": true,
"pint": 2,
"pint8": 3,
"pint16": 4,
"pint32": 5,
"pint64": 6,
"puint": 7,
"puint8": 8,
"puint16": 9,
"puint32": 10,
"puint64": 11,
"puintptr": 12,
"pfloat": 13.1,
"pfloat32": 14.1,
"pfloat64": 15.1,
"string": "",
"pstring": "16",
"map": null,
"mapp": null,
"pmap": {
"17": {
"tag": "tag17"
},
"18": {
"tag": "tag18"
}
},
"pmapp": {
"19": {
"tag": "tag19"
},
"20": null
},
"emptymap": null,
"nilmap": null,
"slice": [],
"slicep": [],
"pslice": [
{
"tag": "tag20"
},
{
"tag": "tag21"
}
],
"pslicep": [
{
"tag": "tag22"
},
null,
{
"tag": "tag23"
}
],
"emptyslice": [],
"nilslice": [],
"stringslice": [],
"byteslice": [],
"small": {
"tag": ""
},
"psmall": null,
"ppsmall": {
"tag": "tag31"
},
"interface": null,
"pinterface": 5.2
}`
var pallValueCompact = strings.Map(noSpace, pallValueIndent)