1
2
3
4
5 package json
6
7 import (
8 "bytes"
9 "os"
10 "reflect"
11 "strings"
12 "testing"
13 )
14
15 type T struct {
16 X string
17 Y int
18 }
19
20 type tx struct {
21 x int
22 }
23
24 var txType = reflect.TypeOf((*tx)(nil)).Elem()
25
26
27
28 type unmarshaler struct {
29 T bool
30 }
31
32 func (u *unmarshaler) UnmarshalJSON(b []byte) os.Error {
33 *u = unmarshaler{true}
34 return nil
35 }
36
37 type ustruct struct {
38 M unmarshaler
39 }
40
41 var (
42 um0, um1 unmarshaler
43 ump = &um1
44 umtrue = unmarshaler{true}
45 umslice = []unmarshaler{unmarshaler{true}}
46 umslicep = new([]unmarshaler)
47 umstruct = ustruct{unmarshaler{true}}
48 )
49
50 type unmarshalTest struct {
51 in string
52 ptr interface{}
53 out interface{}
54 err os.Error
55 }
56
57 var unmarshalTests = []unmarshalTest{
58
59 {`true`, new(bool), true, nil},
60 {`1`, new(int), 1, nil},
61 {`1.2`, new(float64), 1.2, nil},
62 {`-5`, new(int16), int16(-5), nil},
63 {`"a\u1234"`, new(string), "a\u1234", nil},
64 {`"http:\/\/"`, new(string), "http://", nil},
65 {`"g-clef: \uD834\uDD1E"`, new(string), "g-clef: \U0001D11E", nil},
66 {`"invalid: \uD834x\uDD1E"`, new(string), "invalid: \uFFFDx\uFFFD", nil},
67 {"null", new(interface{}), nil, nil},
68 {`{"X": [1,2,3], "Y": 4}`, new(T), T{Y: 4}, &UnmarshalTypeError{"array", reflect.TypeOf("")}},
69 {`{"x": 1}`, new(tx), tx{}, &UnmarshalFieldError{"x", txType, txType.Field(0)}},
70
71
72 {`{"X": "foo", "Y"}`, nil, nil, &SyntaxError{"invalid character '}' after object key", 17}},
73
74
75 {allValueIndent, new(All), allValue, nil},
76 {allValueCompact, new(All), allValue, nil},
77 {allValueIndent, new(*All), &allValue, nil},
78 {allValueCompact, new(*All), &allValue, nil},
79 {pallValueIndent, new(All), pallValue, nil},
80 {pallValueCompact, new(All), pallValue, nil},
81 {pallValueIndent, new(*All), &pallValue, nil},
82 {pallValueCompact, new(*All), &pallValue, nil},
83
84
85 {`{"T":false}`, &um0, umtrue, nil},
86 {`{"T":false}`, &ump, &umtrue, nil},
87 {`[{"T":false}]`, &umslice, umslice, nil},
88 {`[{"T":false}]`, &umslicep, &umslice, nil},
89 {`{"M":{"T":false}}`, &umstruct, umstruct, nil},
90 }
91
92 func TestMarshal(t *testing.T) {
93 b, err := Marshal(allValue)
94 if err != nil {
95 t.Fatalf("Marshal allValue: %v", err)
96 }
97 if string(b) != allValueCompact {
98 t.Errorf("Marshal allValueCompact")
99 diff(t, b, []byte(allValueCompact))
100 return
101 }
102
103 b, err = Marshal(pallValue)
104 if err != nil {
105 t.Fatalf("Marshal pallValue: %v", err)
106 }
107 if string(b) != pallValueCompact {
108 t.Errorf("Marshal pallValueCompact")
109 diff(t, b, []byte(pallValueCompact))
110 return
111 }
112 }
113
114 func TestMarshalBadUTF8(t *testing.T) {
115 s := "hello\xffworld"
116 b, err := Marshal(s)
117 if err == nil {
118 t.Fatal("Marshal bad UTF8: no error")
119 }
120 if len(b) != 0 {
121 t.Fatal("Marshal returned data")
122 }
123 if _, ok := err.(*InvalidUTF8Error); !ok {
124 t.Fatalf("Marshal did not return InvalidUTF8Error: %T %v", err, err)
125 }
126 }
127
128 func TestUnmarshal(t *testing.T) {
129 for i, tt := range unmarshalTests {
130 var scan scanner
131 in := []byte(tt.in)
132 if err := checkValid(in, &scan); err != nil {
133 if !reflect.DeepEqual(err, tt.err) {
134 t.Errorf("#%d: checkValid: %#v", i, err)
135 continue
136 }
137 }
138 if tt.ptr == nil {
139 continue
140 }
141
142 v := reflect.New(reflect.TypeOf(tt.ptr).Elem())
143 if err := Unmarshal([]byte(in), v.Interface()); !reflect.DeepEqual(err, tt.err) {
144 t.Errorf("#%d: %v want %v", i, err, tt.err)
145 continue
146 }
147 if !reflect.DeepEqual(v.Elem().Interface(), tt.out) {
148 t.Errorf("#%d: mismatch\nhave: %#+v\nwant: %#+v", i, v.Elem().Interface(), tt.out)
149 data, _ := Marshal(v.Elem().Interface())
150 println(string(data))
151 data, _ = Marshal(tt.out)
152 println(string(data))
153 continue
154 }
155 }
156 }
157
158 func TestUnmarshalMarshal(t *testing.T) {
159 initBig()
160 var v interface{}
161 if err := Unmarshal(jsonBig, &v); err != nil {
162 t.Fatalf("Unmarshal: %v", err)
163 }
164 b, err := Marshal(v)
165 if err != nil {
166 t.Fatalf("Marshal: %v", err)
167 }
168 if bytes.Compare(jsonBig, b) != 0 {
169 t.Errorf("Marshal jsonBig")
170 diff(t, b, jsonBig)
171 return
172 }
173 }
174
175 func TestLargeByteSlice(t *testing.T) {
176 s0 := make([]byte, 2000)
177 for i := range s0 {
178 s0[i] = byte(i)
179 }
180 b, err := Marshal(s0)
181 if err != nil {
182 t.Fatalf("Marshal: %v", err)
183 }
184 var s1 []byte
185 if err := Unmarshal(b, &s1); err != nil {
186 t.Fatalf("Unmarshal: %v", err)
187 }
188 if bytes.Compare(s0, s1) != 0 {
189 t.Errorf("Marshal large byte slice")
190 diff(t, s0, s1)
191 }
192 }
193
194 type Xint struct {
195 X int
196 }
197
198 func TestUnmarshalInterface(t *testing.T) {
199 var xint Xint
200 var i interface{} = &xint
201 if err := Unmarshal([]byte(`{"X":1}`), &i); err != nil {
202 t.Fatalf("Unmarshal: %v", err)
203 }
204 if xint.X != 1 {
205 t.Fatalf("Did not write to xint")
206 }
207 }
208
209 func TestUnmarshalPtrPtr(t *testing.T) {
210 var xint Xint
211 pxint := &xint
212 if err := Unmarshal([]byte(`{"X":1}`), &pxint); err != nil {
213 t.Fatalf("Unmarshal: %v", err)
214 }
215 if xint.X != 1 {
216 t.Fatalf("Did not write to xint")
217 }
218 }
219
220 func TestEscape(t *testing.T) {
221 const input = `"foobar"<html>`
222 const expected = `"\"foobar\"\u003chtml\u003e"`
223 b, err := Marshal(input)
224 if err != nil {
225 t.Fatalf("Marshal error: %v", err)
226 }
227 if s := string(b); s != expected {
228 t.Errorf("Encoding of [%s] was [%s], want [%s]", input, s, expected)
229 }
230 }
231
232 func TestHTMLEscape(t *testing.T) {
233 b, err := MarshalForHTML("foobarbaz<>&quux")
234 if err != nil {
235 t.Fatalf("MarshalForHTML error: %v", err)
236 }
237 if !bytes.Equal(b, []byte(`"foobarbaz\u003c\u003e\u0026quux"`)) {
238 t.Fatalf("Unexpected encoding of \"<>&\": %s", b)
239 }
240 }
241
242 func noSpace(c int) int {
243 if isSpace(c) {
244 return -1
245 }
246 return c
247 }
248
249 type All struct {
250 Bool bool
251 Int int
252 Int8 int8
253 Int16 int16
254 Int32 int32
255 Int64 int64
256 Uint uint
257 Uint8 uint8
258 Uint16 uint16
259 Uint32 uint32
260 Uint64 uint64
261 Uintptr uintptr
262 Float32 float32
263 Float64 float64
264
265 Foo string `json:"bar"`
266 Foo2 string `json:"bar2,dummyopt"`
267
268 IntStr int64 `json:",string"`
269
270 PBool *bool
271 PInt *int
272 PInt8 *int8
273 PInt16 *int16
274 PInt32 *int32
275 PInt64 *int64
276 PUint *uint
277 PUint8 *uint8
278 PUint16 *uint16
279 PUint32 *uint32
280 PUint64 *uint64
281 PUintptr *uintptr
282 PFloat32 *float32
283 PFloat64 *float64
284
285 String string
286 PString *string
287
288 Map map[string]Small
289 MapP map[string]*Small
290 PMap *map[string]Small
291 PMapP *map[string]*Small
292
293 EmptyMap map[string]Small
294 NilMap map[string]Small
295
296 Slice []Small
297 SliceP []*Small
298 PSlice *[]Small
299 PSliceP *[]*Small
300
301 EmptySlice []Small
302 NilSlice []Small
303
304 StringSlice []string
305 ByteSlice []byte
306
307 Small Small
308 PSmall *Small
309 PPSmall **Small
310
311 Interface interface{}
312 PInterface *interface{}
313
314 unexported int
315 }
316
317 type Small struct {
318 Tag string
319 }
320
321 var allValue = All{
322 Bool: true,
323 Int: 2,
324 Int8: 3,
325 Int16: 4,
326 Int32: 5,
327 Int64: 6,
328 Uint: 7,
329 Uint8: 8,
330 Uint16: 9,
331 Uint32: 10,
332 Uint64: 11,
333 Uintptr: 12,
334 Float32: 14.1,
335 Float64: 15.1,
336 Foo: "foo",
337 Foo2: "foo2",
338 IntStr: 42,
339 String: "16",
340 Map: map[string]Small{
341 "17": {Tag: "tag17"},
342 "18": {Tag: "tag18"},
343 },
344 MapP: map[string]*Small{
345 "19": &Small{Tag: "tag19"},
346 "20": nil,
347 },
348 EmptyMap: map[string]Small{},
349 Slice: []Small{{Tag: "tag20"}, {Tag: "tag21"}},
350 SliceP: []*Small{&Small{Tag: "tag22"}, nil, &Small{Tag: "tag23"}},
351 EmptySlice: []Small{},
352 StringSlice: []string{"str24", "str25", "str26"},
353 ByteSlice: []byte{27, 28, 29},
354 Small: Small{Tag: "tag30"},
355 PSmall: &Small{Tag: "tag31"},
356 Interface: 5.2,
357 }
358
359 var pallValue = All{
360 PBool: &allValue.Bool,
361 PInt: &allValue.Int,
362 PInt8: &allValue.Int8,
363 PInt16: &allValue.Int16,
364 PInt32: &allValue.Int32,
365 PInt64: &allValue.Int64,
366 PUint: &allValue.Uint,
367 PUint8: &allValue.Uint8,
368 PUint16: &allValue.Uint16,
369 PUint32: &allValue.Uint32,
370 PUint64: &allValue.Uint64,
371 PUintptr: &allValue.Uintptr,
372 PFloat32: &allValue.Float32,
373 PFloat64: &allValue.Float64,
374 PString: &allValue.String,
375 PMap: &allValue.Map,
376 PMapP: &allValue.MapP,
377 PSlice: &allValue.Slice,
378 PSliceP: &allValue.SliceP,
379 PPSmall: &allValue.PSmall,
380 PInterface: &allValue.Interface,
381 }
382
383 var allValueIndent = `{
384 "Bool": true,
385 "Int": 2,
386 "Int8": 3,
387 "Int16": 4,
388 "Int32": 5,
389 "Int64": 6,
390 "Uint": 7,
391 "Uint8": 8,
392 "Uint16": 9,
393 "Uint32": 10,
394 "Uint64": 11,
395 "Uintptr": 12,
396 "Float32": 14.1,
397 "Float64": 15.1,
398 "bar": "foo",
399 "bar2": "foo2",
400 "IntStr": "42",
401 "PBool": null,
402 "PInt": null,
403 "PInt8": null,
404 "PInt16": null,
405 "PInt32": null,
406 "PInt64": null,
407 "PUint": null,
408 "PUint8": null,
409 "PUint16": null,
410 "PUint32": null,
411 "PUint64": null,
412 "PUintptr": null,
413 "PFloat32": null,
414 "PFloat64": null,
415 "String": "16",
416 "PString": null,
417 "Map": {
418 "17": {
419 "Tag": "tag17"
420 },
421 "18": {
422 "Tag": "tag18"
423 }
424 },
425 "MapP": {
426 "19": {
427 "Tag": "tag19"
428 },
429 "20": null
430 },
431 "PMap": null,
432 "PMapP": null,
433 "EmptyMap": {},
434 "NilMap": null,
435 "Slice": [
436 {
437 "Tag": "tag20"
438 },
439 {
440 "Tag": "tag21"
441 }
442 ],
443 "SliceP": [
444 {
445 "Tag": "tag22"
446 },
447 null,
448 {
449 "Tag": "tag23"
450 }
451 ],
452 "PSlice": null,
453 "PSliceP": null,
454 "EmptySlice": [],
455 "NilSlice": [],
456 "StringSlice": [
457 "str24",
458 "str25",
459 "str26"
460 ],
461 "ByteSlice": "Gxwd",
462 "Small": {
463 "Tag": "tag30"
464 },
465 "PSmall": {
466 "Tag": "tag31"
467 },
468 "PPSmall": null,
469 "Interface": 5.2,
470 "PInterface": null
471 }`
472
473 var allValueCompact = strings.Map(noSpace, allValueIndent)
474
475 var pallValueIndent = `{
476 "Bool": false,
477 "Int": 0,
478 "Int8": 0,
479 "Int16": 0,
480 "Int32": 0,
481 "Int64": 0,
482 "Uint": 0,
483 "Uint8": 0,
484 "Uint16": 0,
485 "Uint32": 0,
486 "Uint64": 0,
487 "Uintptr": 0,
488 "Float32": 0,
489 "Float64": 0,
490 "bar": "",
491 "bar2": "",
492 "IntStr": "0",
493 "PBool": true,
494 "PInt": 2,
495 "PInt8": 3,
496 "PInt16": 4,
497 "PInt32": 5,
498 "PInt64": 6,
499 "PUint": 7,
500 "PUint8": 8,
501 "PUint16": 9,
502 "PUint32": 10,
503 "PUint64": 11,
504 "PUintptr": 12,
505 "PFloat32": 14.1,
506 "PFloat64": 15.1,
507 "String": "",
508 "PString": "16",
509 "Map": null,
510 "MapP": null,
511 "PMap": {
512 "17": {
513 "Tag": "tag17"
514 },
515 "18": {
516 "Tag": "tag18"
517 }
518 },
519 "PMapP": {
520 "19": {
521 "Tag": "tag19"
522 },
523 "20": null
524 },
525 "EmptyMap": null,
526 "NilMap": null,
527 "Slice": [],
528 "SliceP": [],
529 "PSlice": [
530 {
531 "Tag": "tag20"
532 },
533 {
534 "Tag": "tag21"
535 }
536 ],
537 "PSliceP": [
538 {
539 "Tag": "tag22"
540 },
541 null,
542 {
543 "Tag": "tag23"
544 }
545 ],
546 "EmptySlice": [],
547 "NilSlice": [],
548 "StringSlice": [],
549 "ByteSlice": "",
550 "Small": {
551 "Tag": ""
552 },
553 "PSmall": null,
554 "PPSmall": {
555 "Tag": "tag31"
556 },
557 "Interface": null,
558 "PInterface": 5.2
559 }`
560
561 var pallValueCompact = strings.Map(noSpace, pallValueIndent)