1
2
3
4
5
6
7 package bsonrw
8
9 import (
10 "strings"
11 "testing"
12 "testing/iotest"
13
14 "github.com/google/go-cmp/cmp"
15 )
16
17 func jttDiff(t *testing.T, expected, actual jsonTokenType, desc string) {
18 if diff := cmp.Diff(expected, actual); diff != "" {
19 t.Helper()
20 t.Errorf("%s: Incorrect JSON Token Type (-want, +got): %s\n", desc, diff)
21 t.FailNow()
22 }
23 }
24
25 func jtvDiff(t *testing.T, expected, actual interface{}, desc string) {
26 if diff := cmp.Diff(expected, actual); diff != "" {
27 t.Helper()
28 t.Errorf("%s: Incorrect JSON Token Value (-want, +got): %s\n", desc, diff)
29 t.FailNow()
30 }
31 }
32
33 func expectNilToken(t *testing.T, v *jsonToken, desc string) {
34 if v != nil {
35 t.Helper()
36 t.Errorf("%s: Expected nil JSON token", desc)
37 t.FailNow()
38 }
39 }
40
41 func expectError(t *testing.T, err error, desc string) {
42 if err == nil {
43 t.Helper()
44 t.Errorf("%s: Expected error", desc)
45 t.FailNow()
46 }
47 }
48
49 func expectNoError(t *testing.T, err error, desc string) {
50 if err != nil {
51 t.Helper()
52 t.Errorf("%s: Unepexted error: %v", desc, err)
53 t.FailNow()
54 }
55 }
56
57 type jsonScannerTestCase struct {
58 desc string
59 input string
60 tokens []jsonToken
61 }
62
63
64 const longKey = "abcdefghijklmnopqrstuvwxyz" + "abcdefghijklmnopqrstuvwxyz" +
65 "abcdefghijklmnopqrstuvwxyz" + "abcdefghijklmnopqrstuvwxyz" +
66 "abcdefghijklmnopqrstuvwxyz" + "abcdefghijklmnopqrstuvwxyz" +
67 "abcdefghijklmnopqrstuvwxyz" + "abcdefghijklmnopqrstuvwxyz" +
68 "abcdefghijklmnopqrstuvwxyz" + "abcdefghijklmnopqrstuvwxyz" +
69 "abcdefghijklmnopqrstuvwxyz" + "abcdefghijklmnopqrstuvwxyz" +
70 "abcdefghijklmnopqrstuvwxyz" + "abcdefghijklmnopqrstuvwxyz" +
71 "abcdefghijklmnopqrstuvwxyz" + "abcdefghijklmnopqrstuvwxyz" +
72 "abcdefghijklmnopqrstuvwxyz" + "abcdefghijklmnopqrstuvwxyz" +
73 "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqr"
74
75 func TestJsonScannerValidInputs(t *testing.T) {
76 cases := []jsonScannerTestCase{
77 {
78 desc: "empty input", input: "",
79 tokens: []jsonToken{},
80 },
81 {
82 desc: "empty object", input: "{}",
83 tokens: []jsonToken{{t: jttBeginObject, v: byte('{')}, {t: jttEndObject, v: byte('}')}},
84 },
85 {
86 desc: "empty array", input: "[]",
87 tokens: []jsonToken{{t: jttBeginArray, v: byte('[')}, {t: jttEndArray, v: byte(']')}},
88 },
89 {
90 desc: "valid empty string", input: `""`,
91 tokens: []jsonToken{{t: jttString, v: ""}},
92 },
93 {
94 desc: "valid string--no escaped characters",
95 input: `"string"`,
96 tokens: []jsonToken{{t: jttString, v: "string"}},
97 },
98 {
99 desc: "valid string--escaped characters",
100 input: `"\"\\\/\b\f\n\r\t"`,
101 tokens: []jsonToken{{t: jttString, v: "\"\\/\b\f\n\r\t"}},
102 },
103 {
104 desc: "valid string--surrogate pair",
105 input: `"abc \uD834\uDd1e 123"`,
106 tokens: []jsonToken{{t: jttString, v: "abc 𝄞 123"}},
107 },
108 {
109 desc: "valid string--high surrogate at end of string",
110 input: `"abc \uD834"`,
111 tokens: []jsonToken{{t: jttString, v: "abc �"}},
112 },
113 {
114 desc: "valid string--low surrogate at end of string",
115 input: `"abc \uDD1E"`,
116 tokens: []jsonToken{{t: jttString, v: "abc �"}},
117 },
118 {
119 desc: "valid string--high surrogate with non-surrogate Unicode value",
120 input: `"abc \uDD1E\u00BF"`,
121 tokens: []jsonToken{{t: jttString, v: "abc �¿"}},
122 },
123 {
124 desc: "valid string--high surrogate with non-Unicode escape sequence",
125 input: `"abc \uDD1E\t"`,
126 tokens: []jsonToken{{t: jttString, v: "abc �\t"}},
127 },
128 {
129 desc: "valid literal--true", input: "true",
130 tokens: []jsonToken{{t: jttBool, v: true}},
131 },
132 {
133 desc: "valid literal--false", input: "false",
134 tokens: []jsonToken{{t: jttBool, v: false}},
135 },
136 {
137 desc: "valid literal--null", input: "null",
138 tokens: []jsonToken{{t: jttNull}},
139 },
140 {
141 desc: "valid int32: 0", input: "0",
142 tokens: []jsonToken{{t: jttInt32, v: int32(0)}},
143 },
144 {
145 desc: "valid int32: -0", input: "-0",
146 tokens: []jsonToken{{t: jttInt32, v: int32(0)}},
147 },
148 {
149 desc: "valid int32: 1", input: "1",
150 tokens: []jsonToken{{t: jttInt32, v: int32(1)}},
151 },
152 {
153 desc: "valid int32: -1", input: "-1",
154 tokens: []jsonToken{{t: jttInt32, v: int32(-1)}},
155 },
156 {
157 desc: "valid int32: 10", input: "10",
158 tokens: []jsonToken{{t: jttInt32, v: int32(10)}},
159 },
160 {
161 desc: "valid int32: 1234", input: "1234",
162 tokens: []jsonToken{{t: jttInt32, v: int32(1234)}},
163 },
164 {
165 desc: "valid int32: -10", input: "-10",
166 tokens: []jsonToken{{t: jttInt32, v: int32(-10)}},
167 },
168 {
169 desc: "valid int32: -1234", input: "-1234",
170 tokens: []jsonToken{{t: jttInt32, v: int32(-1234)}},
171 },
172 {
173 desc: "valid int64: 2147483648", input: "2147483648",
174 tokens: []jsonToken{{t: jttInt64, v: int64(2147483648)}},
175 },
176 {
177 desc: "valid int64: -2147483649", input: "-2147483649",
178 tokens: []jsonToken{{t: jttInt64, v: int64(-2147483649)}},
179 },
180 {
181 desc: "valid double: 0.0", input: "0.0",
182 tokens: []jsonToken{{t: jttDouble, v: 0.0}},
183 },
184 {
185 desc: "valid double: -0.0", input: "-0.0",
186 tokens: []jsonToken{{t: jttDouble, v: 0.0}},
187 },
188 {
189 desc: "valid double: 0.1", input: "0.1",
190 tokens: []jsonToken{{t: jttDouble, v: 0.1}},
191 },
192 {
193 desc: "valid double: 0.1234", input: "0.1234",
194 tokens: []jsonToken{{t: jttDouble, v: 0.1234}},
195 },
196 {
197 desc: "valid double: 1.0", input: "1.0",
198 tokens: []jsonToken{{t: jttDouble, v: 1.0}},
199 },
200 {
201 desc: "valid double: -1.0", input: "-1.0",
202 tokens: []jsonToken{{t: jttDouble, v: -1.0}},
203 },
204 {
205 desc: "valid double: 1.234", input: "1.234",
206 tokens: []jsonToken{{t: jttDouble, v: 1.234}},
207 },
208 {
209 desc: "valid double: -1.234", input: "-1.234",
210 tokens: []jsonToken{{t: jttDouble, v: -1.234}},
211 },
212 {
213 desc: "valid double: 1e10", input: "1e10",
214 tokens: []jsonToken{{t: jttDouble, v: 1e+10}},
215 },
216 {
217 desc: "valid double: 1E10", input: "1E10",
218 tokens: []jsonToken{{t: jttDouble, v: 1e+10}},
219 },
220 {
221 desc: "valid double: 1.2e10", input: "1.2e10",
222 tokens: []jsonToken{{t: jttDouble, v: 1.2e+10}},
223 },
224 {
225 desc: "valid double: 1.2E10", input: "1.2E10",
226 tokens: []jsonToken{{t: jttDouble, v: 1.2e+10}},
227 },
228 {
229 desc: "valid double: -1.2e10", input: "-1.2e10",
230 tokens: []jsonToken{{t: jttDouble, v: -1.2e+10}},
231 },
232 {
233 desc: "valid double: -1.2E10", input: "-1.2E10",
234 tokens: []jsonToken{{t: jttDouble, v: -1.2e+10}},
235 },
236 {
237 desc: "valid double: -1.2e+10", input: "-1.2e+10",
238 tokens: []jsonToken{{t: jttDouble, v: -1.2e+10}},
239 },
240 {
241 desc: "valid double: -1.2E+10", input: "-1.2E+10",
242 tokens: []jsonToken{{t: jttDouble, v: -1.2e+10}},
243 },
244 {
245 desc: "valid double: 1.2e-10", input: "1.2e-10",
246 tokens: []jsonToken{{t: jttDouble, v: 1.2e-10}},
247 },
248 {
249 desc: "valid double: 1.2E-10", input: "1.2e-10",
250 tokens: []jsonToken{{t: jttDouble, v: 1.2e-10}},
251 },
252 {
253 desc: "valid double: -1.2e-10", input: "-1.2e-10",
254 tokens: []jsonToken{{t: jttDouble, v: -1.2e-10}},
255 },
256 {
257 desc: "valid double: -1.2E-10", input: "-1.2E-10",
258 tokens: []jsonToken{{t: jttDouble, v: -1.2e-10}},
259 },
260 {
261 desc: "valid double: 8005332285744496613785600", input: "8005332285744496613785600",
262 tokens: []jsonToken{{t: jttDouble, v: float64(8005332285744496613785600)}},
263 },
264 {
265 desc: "valid object, only spaces",
266 input: `{"key": "string", "key2": 2, "key3": {}, "key4": [], "key5": false }`,
267 tokens: []jsonToken{
268 {t: jttBeginObject, v: byte('{')}, {t: jttString, v: "key"}, {t: jttColon, v: byte(':')}, {t: jttString, v: "string"},
269 {t: jttComma, v: byte(',')}, {t: jttString, v: "key2"}, {t: jttColon, v: byte(':')}, {t: jttInt32, v: int32(2)},
270 {t: jttComma, v: byte(',')}, {t: jttString, v: "key3"}, {t: jttColon, v: byte(':')}, {t: jttBeginObject, v: byte('{')}, {t: jttEndObject, v: byte('}')},
271 {t: jttComma, v: byte(',')}, {t: jttString, v: "key4"}, {t: jttColon, v: byte(':')}, {t: jttBeginArray, v: byte('[')}, {t: jttEndArray, v: byte(']')},
272 {t: jttComma, v: byte(',')}, {t: jttString, v: "key5"}, {t: jttColon, v: byte(':')}, {t: jttBool, v: false}, {t: jttEndObject, v: byte('}')},
273 },
274 },
275 {
276 desc: "valid object, mixed whitespace",
277 input: `
278 { "key" : "string"
279 , "key2": 2
280 , "key3": {}
281 , "key4": []
282 , "key5": false
283 }`,
284 tokens: []jsonToken{
285 {t: jttBeginObject, v: byte('{')}, {t: jttString, v: "key"}, {t: jttColon, v: byte(':')}, {t: jttString, v: "string"},
286 {t: jttComma, v: byte(',')}, {t: jttString, v: "key2"}, {t: jttColon, v: byte(':')}, {t: jttInt32, v: int32(2)},
287 {t: jttComma, v: byte(',')}, {t: jttString, v: "key3"}, {t: jttColon, v: byte(':')}, {t: jttBeginObject, v: byte('{')}, {t: jttEndObject, v: byte('}')},
288 {t: jttComma, v: byte(',')}, {t: jttString, v: "key4"}, {t: jttColon, v: byte(':')}, {t: jttBeginArray, v: byte('[')}, {t: jttEndArray, v: byte(']')},
289 {t: jttComma, v: byte(',')}, {t: jttString, v: "key5"}, {t: jttColon, v: byte(':')}, {t: jttBool, v: false}, {t: jttEndObject, v: byte('}')},
290 },
291 },
292 {
293 desc: "input greater than buffer size",
294 input: `{"` + longKey + `": 1}`,
295 tokens: []jsonToken{
296 {t: jttBeginObject, v: byte('{')}, {t: jttString, v: longKey}, {t: jttColon, v: byte(':')},
297 {t: jttInt32, v: int32(1)}, {t: jttEndObject, v: byte('}')},
298 },
299 },
300 }
301
302 for _, tc := range cases {
303 t.Run(tc.desc, func(t *testing.T) {
304 js := &jsonScanner{r: strings.NewReader(tc.input)}
305
306 for _, token := range tc.tokens {
307 c, err := js.nextToken()
308 expectNoError(t, err, tc.desc)
309 jttDiff(t, token.t, c.t, tc.desc)
310 jtvDiff(t, token.v, c.v, tc.desc)
311 }
312
313 c, err := js.nextToken()
314 noerr(t, err)
315 jttDiff(t, jttEOF, c.t, tc.desc)
316
317
318 js = &jsonScanner{r: iotest.DataErrReader(strings.NewReader(tc.input))}
319
320 for _, token := range tc.tokens {
321 c, err := js.nextToken()
322 expectNoError(t, err, tc.desc)
323 jttDiff(t, token.t, c.t, tc.desc)
324 jtvDiff(t, token.v, c.v, tc.desc)
325 }
326
327 c, err = js.nextToken()
328 noerr(t, err)
329 jttDiff(t, jttEOF, c.t, tc.desc)
330 })
331 }
332 }
333
334 func TestJsonScannerInvalidInputs(t *testing.T) {
335 cases := []jsonScannerTestCase{
336 {desc: "missing quotation", input: `"missing`},
337 {desc: "invalid escape character--first character", input: `"\invalid"`},
338 {desc: "invalid escape character--middle", input: `"i\nv\alid"`},
339 {desc: "invalid escape character--single quote", input: `"f\'oo"`},
340 {desc: "invalid literal--trueee", input: "trueee"},
341 {desc: "invalid literal--tire", input: "tire"},
342 {desc: "invalid literal--nulll", input: "nulll"},
343 {desc: "invalid literal--fals", input: "fals"},
344 {desc: "invalid literal--falsee", input: "falsee"},
345 {desc: "invalid literal--fake", input: "fake"},
346 {desc: "invalid literal--bad", input: "bad"},
347 {desc: "invalid number: -", input: "-"},
348 {desc: "invalid number: --0", input: "--0"},
349 {desc: "invalid number: -a", input: "-a"},
350 {desc: "invalid number: 00", input: "00"},
351 {desc: "invalid number: 01", input: "01"},
352 {desc: "invalid number: 0-", input: "0-"},
353 {desc: "invalid number: 1-", input: "1-"},
354 {desc: "invalid number: 0..", input: "0.."},
355 {desc: "invalid number: 0.-", input: "0.-"},
356 {desc: "invalid number: 0..0", input: "0..0"},
357 {desc: "invalid number: 0.1.0", input: "0.1.0"},
358 {desc: "invalid number: 0e", input: "0e"},
359 {desc: "invalid number: 0e.", input: "0e."},
360 {desc: "invalid number: 0e1.", input: "0e1."},
361 {desc: "invalid number: 0e1e", input: "0e1e"},
362 {desc: "invalid number: 0e+.1", input: "0e+.1"},
363 {desc: "invalid number: 0e+1.", input: "0e+1."},
364 {desc: "invalid number: 0e+1e", input: "0e+1e"},
365 }
366
367 for _, tc := range cases {
368 t.Run(tc.desc, func(t *testing.T) {
369 js := &jsonScanner{r: strings.NewReader(tc.input)}
370
371 c, err := js.nextToken()
372 expectNilToken(t, c, tc.desc)
373 expectError(t, err, tc.desc)
374 })
375 }
376 }
377
View as plain text