mirror of
https://github.com/schollz/cowyo.git
synced 2023-08-10 21:13:00 +03:00
219 lines
6.4 KiB
Go
219 lines
6.4 KiB
Go
|
package json
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"fmt"
|
||
|
"reflect"
|
||
|
"strconv"
|
||
|
"testing"
|
||
|
)
|
||
|
|
||
|
type funcN struct {
|
||
|
Arg1 int `json:"arg1"`
|
||
|
Arg2 int `json:"arg2"`
|
||
|
}
|
||
|
|
||
|
type funcs struct {
|
||
|
Func2 *funcN `json:"$func2"`
|
||
|
Func1 *funcN `json:"$func1"`
|
||
|
}
|
||
|
|
||
|
type funcsText struct {
|
||
|
Func1 jsonText `json:"$func1"`
|
||
|
Func2 jsonText `json:"$func2"`
|
||
|
}
|
||
|
|
||
|
type jsonText struct {
|
||
|
json string
|
||
|
}
|
||
|
|
||
|
func (jt *jsonText) UnmarshalJSON(data []byte) error {
|
||
|
jt.json = string(data)
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
type nestedText struct {
|
||
|
F jsonText
|
||
|
B bool
|
||
|
}
|
||
|
|
||
|
type unquotedKey struct {
|
||
|
S string `json:"$k_1"`
|
||
|
}
|
||
|
|
||
|
var ext Extension
|
||
|
|
||
|
type keyed string
|
||
|
|
||
|
func decodeKeyed(data []byte) (interface{}, error) {
|
||
|
return keyed(data), nil
|
||
|
}
|
||
|
|
||
|
type keyedType struct {
|
||
|
K keyed
|
||
|
I int
|
||
|
}
|
||
|
|
||
|
type docint int
|
||
|
|
||
|
type const1Type struct{}
|
||
|
|
||
|
var const1 = new(const1Type)
|
||
|
|
||
|
func init() {
|
||
|
ext.DecodeFunc("Func1", "$func1")
|
||
|
ext.DecodeFunc("Func2", "$func2", "arg1", "arg2")
|
||
|
ext.DecodeFunc("Func3", "$func3", "arg1")
|
||
|
ext.DecodeFunc("new Func4", "$func4", "arg1")
|
||
|
|
||
|
ext.DecodeConst("Const1", const1)
|
||
|
|
||
|
ext.DecodeKeyed("$key1", decodeKeyed)
|
||
|
ext.DecodeKeyed("$func3", decodeKeyed)
|
||
|
|
||
|
ext.EncodeType(docint(0), func(v interface{}) ([]byte, error) {
|
||
|
s := `{"$docint": ` + strconv.Itoa(int(v.(docint))) + `}`
|
||
|
return []byte(s), nil
|
||
|
})
|
||
|
|
||
|
ext.DecodeUnquotedKeys(true)
|
||
|
ext.DecodeTrailingCommas(true)
|
||
|
}
|
||
|
|
||
|
type extDecodeTest struct {
|
||
|
in string
|
||
|
ptr interface{}
|
||
|
out interface{}
|
||
|
err error
|
||
|
|
||
|
noext bool
|
||
|
}
|
||
|
|
||
|
var extDecodeTests = []extDecodeTest{
|
||
|
// Functions
|
||
|
{in: `Func1()`, ptr: new(interface{}), out: map[string]interface{}{
|
||
|
"$func1": map[string]interface{}{},
|
||
|
}},
|
||
|
{in: `{"v": Func1()}`, ptr: new(interface{}), out: map[string]interface{}{
|
||
|
"v": map[string]interface{}{"$func1": map[string]interface{}{}},
|
||
|
}},
|
||
|
{in: `Func2(1)`, ptr: new(interface{}), out: map[string]interface{}{
|
||
|
"$func2": map[string]interface{}{"arg1": float64(1)},
|
||
|
}},
|
||
|
{in: `Func2(1, 2)`, ptr: new(interface{}), out: map[string]interface{}{
|
||
|
"$func2": map[string]interface{}{"arg1": float64(1), "arg2": float64(2)},
|
||
|
}},
|
||
|
{in: `Func2(Func1())`, ptr: new(interface{}), out: map[string]interface{}{
|
||
|
"$func2": map[string]interface{}{"arg1": map[string]interface{}{"$func1": map[string]interface{}{}}},
|
||
|
}},
|
||
|
{in: `Func2(1, 2, 3)`, ptr: new(interface{}), err: fmt.Errorf("json: too many arguments for function Func2")},
|
||
|
{in: `BadFunc()`, ptr: new(interface{}), err: fmt.Errorf(`json: unknown function "BadFunc"`)},
|
||
|
|
||
|
{in: `Func1()`, ptr: new(funcs), out: funcs{Func1: &funcN{}}},
|
||
|
{in: `Func2(1)`, ptr: new(funcs), out: funcs{Func2: &funcN{Arg1: 1}}},
|
||
|
{in: `Func2(1, 2)`, ptr: new(funcs), out: funcs{Func2: &funcN{Arg1: 1, Arg2: 2}}},
|
||
|
|
||
|
{in: `Func2(1, 2, 3)`, ptr: new(funcs), err: fmt.Errorf("json: too many arguments for function Func2")},
|
||
|
{in: `BadFunc()`, ptr: new(funcs), err: fmt.Errorf(`json: unknown function "BadFunc"`)},
|
||
|
|
||
|
{in: `Func2(1)`, ptr: new(jsonText), out: jsonText{"Func2(1)"}},
|
||
|
{in: `Func2(1, 2)`, ptr: new(funcsText), out: funcsText{Func2: jsonText{"Func2(1, 2)"}}},
|
||
|
{in: `{"f": Func2(1, 2), "b": true}`, ptr: new(nestedText), out: nestedText{jsonText{"Func2(1, 2)"}, true}},
|
||
|
|
||
|
{in: `Func1()`, ptr: new(struct{}), out: struct{}{}},
|
||
|
|
||
|
// Functions with "new" prefix
|
||
|
{in: `new Func4(1)`, ptr: new(interface{}), out: map[string]interface{}{
|
||
|
"$func4": map[string]interface{}{"arg1": float64(1)},
|
||
|
}},
|
||
|
|
||
|
// Constants
|
||
|
{in: `Const1`, ptr: new(interface{}), out: const1},
|
||
|
{in: `{"c": Const1}`, ptr: new(struct{ C *const1Type }), out: struct{ C *const1Type }{const1}},
|
||
|
|
||
|
// Keyed documents
|
||
|
{in: `{"v": {"$key1": 1}}`, ptr: new(interface{}), out: map[string]interface{}{"v": keyed(`{"$key1": 1}`)}},
|
||
|
{in: `{"k": {"$key1": 1}}`, ptr: new(keyedType), out: keyedType{K: keyed(`{"$key1": 1}`)}},
|
||
|
{in: `{"i": {"$key1": 1}}`, ptr: new(keyedType), err: &UnmarshalTypeError{"object", reflect.TypeOf(0), 18}},
|
||
|
|
||
|
// Keyed function documents
|
||
|
{in: `{"v": Func3()}`, ptr: new(interface{}), out: map[string]interface{}{"v": keyed(`Func3()`)}},
|
||
|
{in: `{"k": Func3()}`, ptr: new(keyedType), out: keyedType{K: keyed(`Func3()`)}},
|
||
|
{in: `{"i": Func3()}`, ptr: new(keyedType), err: &UnmarshalTypeError{"object", reflect.TypeOf(0), 13}},
|
||
|
|
||
|
// Unquoted keys
|
||
|
{in: `{$k_1: "bar"}`, ptr: new(interface{}), out: map[string]interface{}{"$k_1": "bar"}},
|
||
|
{in: `{$k_1: "bar"}`, ptr: new(unquotedKey), out: unquotedKey{"bar"}},
|
||
|
|
||
|
{in: `{$k_1: "bar"}`, noext: true, ptr: new(interface{}),
|
||
|
err: &SyntaxError{"invalid character '$' looking for beginning of object key string", 2}},
|
||
|
{in: `{$k_1: "bar"}`, noext: true, ptr: new(unquotedKey),
|
||
|
err: &SyntaxError{"invalid character '$' looking for beginning of object key string", 2}},
|
||
|
|
||
|
// Trailing commas
|
||
|
{in: `{"k": "v",}`, ptr: new(interface{}), out: map[string]interface{}{"k": "v"}},
|
||
|
{in: `{"k": "v",}`, ptr: new(struct{}), out: struct{}{}},
|
||
|
{in: `["v",]`, ptr: new(interface{}), out: []interface{}{"v"}},
|
||
|
|
||
|
{in: `{"k": "v",}`, noext: true, ptr: new(interface{}),
|
||
|
err: &SyntaxError{"invalid character '}' looking for beginning of object key string", 11}},
|
||
|
{in: `{"k": "v",}`, noext: true, ptr: new(struct{}),
|
||
|
err: &SyntaxError{"invalid character '}' looking for beginning of object key string", 11}},
|
||
|
{in: `["a",]`, noext: true, ptr: new(interface{}),
|
||
|
err: &SyntaxError{"invalid character ']' looking for beginning of value", 6}},
|
||
|
}
|
||
|
|
||
|
type extEncodeTest struct {
|
||
|
in interface{}
|
||
|
out string
|
||
|
err error
|
||
|
}
|
||
|
|
||
|
var extEncodeTests = []extEncodeTest{
|
||
|
{in: docint(13), out: "{\"$docint\":13}\n"},
|
||
|
}
|
||
|
|
||
|
func TestExtensionDecode(t *testing.T) {
|
||
|
for i, tt := range extDecodeTests {
|
||
|
in := []byte(tt.in)
|
||
|
|
||
|
// v = new(right-type)
|
||
|
v := reflect.New(reflect.TypeOf(tt.ptr).Elem())
|
||
|
dec := NewDecoder(bytes.NewReader(in))
|
||
|
if !tt.noext {
|
||
|
dec.Extend(&ext)
|
||
|
}
|
||
|
if err := dec.Decode(v.Interface()); !reflect.DeepEqual(err, tt.err) {
|
||
|
t.Errorf("#%d: %v, want %v", i, err, tt.err)
|
||
|
continue
|
||
|
} else if err != nil {
|
||
|
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())
|
||
|
t.Logf("%s", string(data))
|
||
|
data, _ = Marshal(tt.out)
|
||
|
t.Logf("%s", string(data))
|
||
|
continue
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestExtensionEncode(t *testing.T) {
|
||
|
var buf bytes.Buffer
|
||
|
for i, tt := range extEncodeTests {
|
||
|
buf.Truncate(0)
|
||
|
enc := NewEncoder(&buf)
|
||
|
enc.Extend(&ext)
|
||
|
err := enc.Encode(tt.in)
|
||
|
if !reflect.DeepEqual(err, tt.err) {
|
||
|
t.Errorf("#%d: %v, want %v", i, err, tt.err)
|
||
|
continue
|
||
|
}
|
||
|
if buf.String() != tt.out {
|
||
|
t.Errorf("#%d: mismatch\nhave: %q\nwant: %q", i, buf.String(), tt.out)
|
||
|
}
|
||
|
}
|
||
|
}
|