Home | History | Annotate | Download | only in json
      1 // Copyright 2010 The Go Authors.  All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 package json
      6 
      7 import (
      8 	"bytes"
      9 	"math"
     10 	"math/rand"
     11 	"reflect"
     12 	"testing"
     13 )
     14 
     15 // Tests of simple examples.
     16 
     17 type example struct {
     18 	compact string
     19 	indent  string
     20 }
     21 
     22 var examples = []example{
     23 	{`1`, `1`},
     24 	{`{}`, `{}`},
     25 	{`[]`, `[]`},
     26 	{`{"":2}`, "{\n\t\"\": 2\n}"},
     27 	{`[3]`, "[\n\t3\n]"},
     28 	{`[1,2,3]`, "[\n\t1,\n\t2,\n\t3\n]"},
     29 	{`{"x":1}`, "{\n\t\"x\": 1\n}"},
     30 	{ex1, ex1i},
     31 }
     32 
     33 var ex1 = `[true,false,null,"x",1,1.5,0,-5e+2]`
     34 
     35 var ex1i = `[
     36 	true,
     37 	false,
     38 	null,
     39 	"x",
     40 	1,
     41 	1.5,
     42 	0,
     43 	-5e+2
     44 ]`
     45 
     46 func TestCompact(t *testing.T) {
     47 	var buf bytes.Buffer
     48 	for _, tt := range examples {
     49 		buf.Reset()
     50 		if err := Compact(&buf, []byte(tt.compact)); err != nil {
     51 			t.Errorf("Compact(%#q): %v", tt.compact, err)
     52 		} else if s := buf.String(); s != tt.compact {
     53 			t.Errorf("Compact(%#q) = %#q, want original", tt.compact, s)
     54 		}
     55 
     56 		buf.Reset()
     57 		if err := Compact(&buf, []byte(tt.indent)); err != nil {
     58 			t.Errorf("Compact(%#q): %v", tt.indent, err)
     59 			continue
     60 		} else if s := buf.String(); s != tt.compact {
     61 			t.Errorf("Compact(%#q) = %#q, want %#q", tt.indent, s, tt.compact)
     62 		}
     63 	}
     64 }
     65 
     66 func TestCompactSeparators(t *testing.T) {
     67 	// U+2028 and U+2029 should be escaped inside strings.
     68 	// They should not appear outside strings.
     69 	tests := []struct {
     70 		in, compact string
     71 	}{
     72 		{"{\"\u2028\": 1}", `{"\u2028":1}`},
     73 		{"{\"\u2029\" :2}", `{"\u2029":2}`},
     74 	}
     75 	for _, tt := range tests {
     76 		var buf bytes.Buffer
     77 		if err := Compact(&buf, []byte(tt.in)); err != nil {
     78 			t.Errorf("Compact(%q): %v", tt.in, err)
     79 		} else if s := buf.String(); s != tt.compact {
     80 			t.Errorf("Compact(%q) = %q, want %q", tt.in, s, tt.compact)
     81 		}
     82 	}
     83 }
     84 
     85 func TestIndent(t *testing.T) {
     86 	var buf bytes.Buffer
     87 	for _, tt := range examples {
     88 		buf.Reset()
     89 		if err := Indent(&buf, []byte(tt.indent), "", "\t"); err != nil {
     90 			t.Errorf("Indent(%#q): %v", tt.indent, err)
     91 		} else if s := buf.String(); s != tt.indent {
     92 			t.Errorf("Indent(%#q) = %#q, want original", tt.indent, s)
     93 		}
     94 
     95 		buf.Reset()
     96 		if err := Indent(&buf, []byte(tt.compact), "", "\t"); err != nil {
     97 			t.Errorf("Indent(%#q): %v", tt.compact, err)
     98 			continue
     99 		} else if s := buf.String(); s != tt.indent {
    100 			t.Errorf("Indent(%#q) = %#q, want %#q", tt.compact, s, tt.indent)
    101 		}
    102 	}
    103 }
    104 
    105 // Tests of a large random structure.
    106 
    107 func TestCompactBig(t *testing.T) {
    108 	initBig()
    109 	var buf bytes.Buffer
    110 	if err := Compact(&buf, jsonBig); err != nil {
    111 		t.Fatalf("Compact: %v", err)
    112 	}
    113 	b := buf.Bytes()
    114 	if !bytes.Equal(b, jsonBig) {
    115 		t.Error("Compact(jsonBig) != jsonBig")
    116 		diff(t, b, jsonBig)
    117 		return
    118 	}
    119 }
    120 
    121 func TestIndentBig(t *testing.T) {
    122 	initBig()
    123 	var buf bytes.Buffer
    124 	if err := Indent(&buf, jsonBig, "", "\t"); err != nil {
    125 		t.Fatalf("Indent1: %v", err)
    126 	}
    127 	b := buf.Bytes()
    128 	if len(b) == len(jsonBig) {
    129 		// jsonBig is compact (no unnecessary spaces);
    130 		// indenting should make it bigger
    131 		t.Fatalf("Indent(jsonBig) did not get bigger")
    132 	}
    133 
    134 	// should be idempotent
    135 	var buf1 bytes.Buffer
    136 	if err := Indent(&buf1, b, "", "\t"); err != nil {
    137 		t.Fatalf("Indent2: %v", err)
    138 	}
    139 	b1 := buf1.Bytes()
    140 	if !bytes.Equal(b1, b) {
    141 		t.Error("Indent(Indent(jsonBig)) != Indent(jsonBig)")
    142 		diff(t, b1, b)
    143 		return
    144 	}
    145 
    146 	// should get back to original
    147 	buf1.Reset()
    148 	if err := Compact(&buf1, b); err != nil {
    149 		t.Fatalf("Compact: %v", err)
    150 	}
    151 	b1 = buf1.Bytes()
    152 	if !bytes.Equal(b1, jsonBig) {
    153 		t.Error("Compact(Indent(jsonBig)) != jsonBig")
    154 		diff(t, b1, jsonBig)
    155 		return
    156 	}
    157 }
    158 
    159 type indentErrorTest struct {
    160 	in  string
    161 	err error
    162 }
    163 
    164 var indentErrorTests = []indentErrorTest{
    165 	{`{"X": "foo", "Y"}`, &SyntaxError{"invalid character '}' after object key", 17}},
    166 	{`{"X": "foo" "Y": "bar"}`, &SyntaxError{"invalid character '\"' after object key:value pair", 13}},
    167 }
    168 
    169 func TestIndentErrors(t *testing.T) {
    170 	for i, tt := range indentErrorTests {
    171 		slice := make([]uint8, 0)
    172 		buf := bytes.NewBuffer(slice)
    173 		if err := Indent(buf, []uint8(tt.in), "", ""); err != nil {
    174 			if !reflect.DeepEqual(err, tt.err) {
    175 				t.Errorf("#%d: Indent: %#v", i, err)
    176 				continue
    177 			}
    178 		}
    179 	}
    180 }
    181 
    182 func TestNextValueBig(t *testing.T) {
    183 	initBig()
    184 	var scan scanner
    185 	item, rest, err := nextValue(jsonBig, &scan)
    186 	if err != nil {
    187 		t.Fatalf("nextValue: %s", err)
    188 	}
    189 	if len(item) != len(jsonBig) || &item[0] != &jsonBig[0] {
    190 		t.Errorf("invalid item: %d %d", len(item), len(jsonBig))
    191 	}
    192 	if len(rest) != 0 {
    193 		t.Errorf("invalid rest: %d", len(rest))
    194 	}
    195 
    196 	item, rest, err = nextValue(append(jsonBig, "HELLO WORLD"...), &scan)
    197 	if err != nil {
    198 		t.Fatalf("nextValue extra: %s", err)
    199 	}
    200 	if len(item) != len(jsonBig) {
    201 		t.Errorf("invalid item: %d %d", len(item), len(jsonBig))
    202 	}
    203 	if string(rest) != "HELLO WORLD" {
    204 		t.Errorf("invalid rest: %d", len(rest))
    205 	}
    206 }
    207 
    208 var benchScan scanner
    209 
    210 func BenchmarkSkipValue(b *testing.B) {
    211 	initBig()
    212 	b.ResetTimer()
    213 	for i := 0; i < b.N; i++ {
    214 		nextValue(jsonBig, &benchScan)
    215 	}
    216 	b.SetBytes(int64(len(jsonBig)))
    217 }
    218 
    219 func diff(t *testing.T, a, b []byte) {
    220 	for i := 0; ; i++ {
    221 		if i >= len(a) || i >= len(b) || a[i] != b[i] {
    222 			j := i - 10
    223 			if j < 0 {
    224 				j = 0
    225 			}
    226 			t.Errorf("diverge at %d: %s vs %s", i, trim(a[j:]), trim(b[j:]))
    227 			return
    228 		}
    229 	}
    230 }
    231 
    232 func trim(b []byte) []byte {
    233 	if len(b) > 20 {
    234 		return b[0:20]
    235 	}
    236 	return b
    237 }
    238 
    239 // Generate a random JSON object.
    240 
    241 var jsonBig []byte
    242 
    243 func initBig() {
    244 	n := 10000
    245 	if testing.Short() {
    246 		n = 100
    247 	}
    248 	b, err := Marshal(genValue(n))
    249 	if err != nil {
    250 		panic(err)
    251 	}
    252 	jsonBig = b
    253 }
    254 
    255 func genValue(n int) interface{} {
    256 	if n > 1 {
    257 		switch rand.Intn(2) {
    258 		case 0:
    259 			return genArray(n)
    260 		case 1:
    261 			return genMap(n)
    262 		}
    263 	}
    264 	switch rand.Intn(3) {
    265 	case 0:
    266 		return rand.Intn(2) == 0
    267 	case 1:
    268 		return rand.NormFloat64()
    269 	case 2:
    270 		return genString(30)
    271 	}
    272 	panic("unreachable")
    273 }
    274 
    275 func genString(stddev float64) string {
    276 	n := int(math.Abs(rand.NormFloat64()*stddev + stddev/2))
    277 	c := make([]rune, n)
    278 	for i := range c {
    279 		f := math.Abs(rand.NormFloat64()*64 + 32)
    280 		if f > 0x10ffff {
    281 			f = 0x10ffff
    282 		}
    283 		c[i] = rune(f)
    284 	}
    285 	return string(c)
    286 }
    287 
    288 func genArray(n int) []interface{} {
    289 	f := int(math.Abs(rand.NormFloat64()) * math.Min(10, float64(n/2)))
    290 	if f > n {
    291 		f = n
    292 	}
    293 	if f < 1 {
    294 		f = 1
    295 	}
    296 	x := make([]interface{}, f)
    297 	for i := range x {
    298 		x[i] = genValue(((i+1)*n)/f - (i*n)/f)
    299 	}
    300 	return x
    301 }
    302 
    303 func genMap(n int) map[string]interface{} {
    304 	f := int(math.Abs(rand.NormFloat64()) * math.Min(10, float64(n/2)))
    305 	if f > n {
    306 		f = n
    307 	}
    308 	if n > 0 && f == 0 {
    309 		f = 1
    310 	}
    311 	x := make(map[string]interface{})
    312 	for i := 0; i < f; i++ {
    313 		x[genString(10)] = genValue(((i+1)*n)/f - (i*n)/f)
    314 	}
    315 	return x
    316 }
    317