1
2
3
4
5 package json
6
7 import (
8 "reflect"
9 "strings"
10 "testing"
11 "unicode/utf16"
12 )
13
14 type rawValueTestdataEntry struct {
15 name testName
16 in string
17 wantValid bool
18 wantCompacted string
19 wantCompactErr error
20 wantIndented string
21 wantIndentErr error
22 wantCanonicalized string
23 wantCanonicalizeErr error
24 }
25
26 var rawValueTestdata = append(func() (out []rawValueTestdataEntry) {
27
28 for _, td := range coderTestdata {
29
30
31 if td.name.name == "ComplicatedString" {
32 td.outCompacted = strings.TrimSpace(td.in)
33 }
34 out = append(out, rawValueTestdataEntry{
35 name: td.name,
36 in: td.in,
37 wantValid: true,
38 wantCompacted: td.outCompacted,
39 wantIndented: td.outIndented,
40 wantCanonicalized: td.outCanonicalized,
41 })
42 }
43 return out
44 }(), []rawValueTestdataEntry{{
45 name: name("RFC8785/Primitives"),
46 in: `{
47 "numbers": [333333333.33333329, 1E30, 4.50,
48 2e-3, 0.000000000000000000000000001],
49 "string": "\u20ac$\u000F\u000aA'\u0042\u0022\u005c\\\"\/",
50 "literals": [null, true, false]
51 }`,
52 wantValid: true,
53 wantCompacted: `{"numbers":[333333333.33333329,1E30,4.50,2e-3,0.000000000000000000000000001],"string":"\u20ac$\u000F\u000aA'\u0042\u0022\u005c\\\"\/","literals":[null,true,false]}`,
54 wantIndented: `{
55 "numbers": [
56 333333333.33333329,
57 1E30,
58 4.50,
59 2e-3,
60 0.000000000000000000000000001
61 ],
62 "string": "\u20ac$\u000F\u000aA'\u0042\u0022\u005c\\\"\/",
63 "literals": [
64 null,
65 true,
66 false
67 ]
68 }`,
69 wantCanonicalized: `{"literals":[null,true,false],"numbers":[333333333.3333333,1e+30,4.5,0.002,1e-27],"string":"€$\u000f\nA'B\"\\\\\"/"}`,
70 }, {
71 name: name("RFC8785/ObjectOrdering"),
72 in: `{
73 "\u20ac": "Euro Sign",
74 "\r": "Carriage Return",
75 "\ufb33": "Hebrew Letter Dalet With Dagesh",
76 "1": "One",
77 "\ud83d\ude00": "Emoji: Grinning Face",
78 "\u0080": "Control",
79 "\u00f6": "Latin Small Letter O With Diaeresis"
80 }`,
81 wantValid: true,
82 wantCompacted: `{"\u20ac":"Euro Sign","\r":"Carriage Return","\ufb33":"Hebrew Letter Dalet With Dagesh","1":"One","\ud83d\ude00":"Emoji: Grinning Face","\u0080":"Control","\u00f6":"Latin Small Letter O With Diaeresis"}`,
83 wantIndented: `{
84 "\u20ac": "Euro Sign",
85 "\r": "Carriage Return",
86 "\ufb33": "Hebrew Letter Dalet With Dagesh",
87 "1": "One",
88 "\ud83d\ude00": "Emoji: Grinning Face",
89 "\u0080": "Control",
90 "\u00f6": "Latin Small Letter O With Diaeresis"
91 }`,
92 wantCanonicalized: `{"\r":"Carriage Return","1":"One","":"Control","ö":"Latin Small Letter O With Diaeresis","€":"Euro Sign","😀":"Emoji: Grinning Face","דּ":"Hebrew Letter Dalet With Dagesh"}`,
93 }, {
94 name: name("LargeIntegers"),
95 in: ` [ -9223372036854775808 , 9223372036854775807 ] `,
96 wantValid: true,
97 wantCompacted: `[-9223372036854775808,9223372036854775807]`,
98 wantIndented: `[
99 -9223372036854775808,
100 9223372036854775807
101 ]`,
102 wantCanonicalized: `[-9223372036854776000,9223372036854776000]`,
103 }, {
104 name: name("InvalidUTF8"),
105 in: ` "living` + "\xde\xad\xbe\xef" + `\ufffd�" `,
106 wantValid: false,
107 wantCompacted: `"living` + "\xde\xad\xbe\xef" + `\ufffd�"`,
108 wantCanonicalizeErr: &SyntacticError{str: "invalid UTF-8 within string"},
109 }, {
110 name: name("InvalidUTF8/SurrogateHalf"),
111 in: `"\ud800"`,
112 wantValid: false,
113 wantCompacted: `"\ud800"`,
114 wantCanonicalizeErr: &SyntacticError{str: `invalid escape sequence "\"" within string`},
115 }, {
116 name: name("UppercaseEscaped"),
117 in: `"\u000B"`,
118 wantValid: true,
119 wantCompacted: `"\u000B"`,
120 wantCanonicalized: `"\u000b"`,
121 }, {
122 name: name("DuplicateNames"),
123 in: ` { "0" : 0 , "1" : 1 , "0" : 0 }`,
124 wantValid: false,
125 wantCompacted: `{"0":0,"1":1,"0":0}`,
126 wantIndented: `{
127 "0": 0,
128 "1": 1,
129 "0": 0
130 }`,
131 wantCanonicalizeErr: &SyntacticError{str: `duplicate name "0" in object`},
132 }}...)
133
134 func TestRawValueMethods(t *testing.T) {
135 for _, td := range rawValueTestdata {
136 t.Run(td.name.name, func(t *testing.T) {
137 if td.wantIndented == "" {
138 td.wantIndented = td.wantCompacted
139 }
140 if td.wantCanonicalized == "" {
141 td.wantCanonicalized = td.wantCompacted
142 }
143 if td.wantCompactErr != nil {
144 td.wantCompacted = td.in
145 }
146 if td.wantIndentErr != nil {
147 td.wantIndented = td.in
148 }
149 if td.wantCanonicalizeErr != nil {
150 td.wantCanonicalized = td.in
151 }
152
153 v := RawValue(td.in)
154 gotValid := v.IsValid()
155 if gotValid != td.wantValid {
156 t.Errorf("%s: RawValue.IsValid = %v, want %v", td.name.where, gotValid, td.wantValid)
157 }
158
159 gotCompacted := RawValue(td.in)
160 gotCompactErr := gotCompacted.Compact()
161 if string(gotCompacted) != td.wantCompacted {
162 t.Errorf("%s: RawValue.Compact = %s, want %s", td.name.where, gotCompacted, td.wantCompacted)
163 }
164 if !reflect.DeepEqual(gotCompactErr, td.wantCompactErr) {
165 t.Errorf("%s: RawValue.Compact error mismatch: got %#v, want %#v", td.name.where, gotCompactErr, td.wantCompactErr)
166 }
167
168 gotIndented := RawValue(td.in)
169 gotIndentErr := gotIndented.Indent("\t", " ")
170 if string(gotIndented) != td.wantIndented {
171 t.Errorf("%s: RawValue.Indent = %s, want %s", td.name.where, gotIndented, td.wantIndented)
172 }
173 if !reflect.DeepEqual(gotIndentErr, td.wantIndentErr) {
174 t.Errorf("%s: RawValue.Indent error mismatch: got %#v, want %#v", td.name.where, gotIndentErr, td.wantIndentErr)
175 }
176
177 gotCanonicalized := RawValue(td.in)
178 gotCanonicalizeErr := gotCanonicalized.Canonicalize()
179 if string(gotCanonicalized) != td.wantCanonicalized {
180 t.Errorf("%s: RawValue.Canonicalize = %s, want %s", td.name.where, gotCanonicalized, td.wantCanonicalized)
181 }
182 if !reflect.DeepEqual(gotCanonicalizeErr, td.wantCanonicalizeErr) {
183 t.Errorf("%s: RawValue.Canonicalize error mismatch: got %#v, want %#v", td.name.where, gotCanonicalizeErr, td.wantCanonicalizeErr)
184 }
185 })
186 }
187 }
188
189 var lessUTF16Testdata = []string{"", "\r", "1", "\u0080", "\u00f6", "\u20ac", "\U0001f600", "\ufb33"}
190
191 func TestLessUTF16(t *testing.T) {
192 for i, si := range lessUTF16Testdata {
193 for j, sj := range lessUTF16Testdata {
194 got := lessUTF16([]byte(si), []byte(sj))
195 want := i < j
196 if got != want {
197 t.Errorf("lessUTF16(%q, %q) = %v, want %v", si, sj, got, want)
198 }
199 }
200 }
201 }
202
203 func FuzzLessUTF16(f *testing.F) {
204 for _, td1 := range lessUTF16Testdata {
205 for _, td2 := range lessUTF16Testdata {
206 f.Add([]byte(td1), []byte(td2))
207 }
208 }
209
210
211
212
213 lessUTF16Simple := func(x, y []byte) bool {
214 ux := utf16.Encode([]rune(string(x)))
215 uy := utf16.Encode([]rune(string(y)))
216
217 for {
218 if len(ux) == 0 || len(uy) == 0 {
219 if len(ux) == len(uy) {
220 return string(x) < string(y)
221 }
222 return len(ux) < len(uy)
223 }
224 if ux[0] != uy[0] {
225 return ux[0] < uy[0]
226 }
227 ux, uy = ux[1:], uy[1:]
228 }
229 }
230
231 f.Fuzz(func(t *testing.T, s1, s2 []byte) {
232
233 got := lessUTF16(s1, s2)
234 want := lessUTF16Simple(s1, s2)
235 if got != want {
236 t.Errorf("lessUTF16(%q, %q) = %v, want %v", s1, s2, got, want)
237 }
238 })
239 }
240
View as plain text