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