Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(53)

Side by Side Diff: tests/lib/convert/json_utf8_chunk_test.dart

Issue 649113005: Make JSON parsing work as a chunked conversion sink. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Address comments. Fix bug. Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « tests/lib/convert/json_test.dart ('k') | tests/lib/convert/unicode_tests.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 library test;
6
7 import "package:expect/expect.dart";
8 import "dart:convert";
9 import "unicode_tests.dart" show UNICODE_TESTS;
10
11 bool badFormat(e) => e is FormatException;
12
13 main() {
14 testNumbers();
15 testStrings();
16 testKeywords();
17 testAll();
18 testMalformed();
19 testUnicodeTests();
20 }
21
22 // Create an UTF-8 sink from a chunked JSON decoder, then let [action]
23 // put data into it, and check that what comes out is equal to [expect].
24 void jsonTest(testName, expect, action(sink), [bool allowMalformed = false]) {
25 jsonParse(testName, (value) {
26 Expect.equals(expect, value, "$testName:$value");
27 }, action, allowMalformed);
28 }
29
30 void jsonParse(testName, check, action, [bool allowMalformed = false]) {
31 var sink = new ChunkedConversionSink.withCallback((values) {
32 var value = values[0];
33 check(value);
34 });
35 var decoderSink = JSON.decoder.startChunkedConversion(sink)
36 .asUtf8Sink(allowMalformed);
37 try {
38 action(decoderSink);
39 } on FormatException catch (e, s) {
40 print("Source: ${e.source} @ ${e.offset}");
41 Expect.fail("Unexpected throw($testName): $e\n$s");
42 }
43 }
44
45 void testStrings() {
46 // String literal containing characters, all escape types,
47 // and a number of UTF-8 encoded characters.
48 var s = r'"abc\f\ndef\r\t\b\"\/\\\u0001\u9999\uffff'
49 '\x7f\xc2\x80\xdf\xbf\xe0\xa0\x80\xef\xbf\xbf'
50 '\xf0\x90\x80\x80\xf4\x8f\xbf\xbf"'; // UTF-8.
51 var expected = "abc\f\ndef\r\t\b\"\/\\\u0001\u9999\uffff"
52 "\x7f\x80\u07ff\u0800\uffff"
53 "\u{10000}\u{10ffff}";
54 for (var i = 1; i < s.length - 1; i++) {
55 var s1 = s.substring(0, i);
56 var s2 = s.substring(i);
57 jsonTest("$s1|$s2-$i", expected, (sink) {
58 sink.add(s1.codeUnits);
59 sink.add(s2.codeUnits);
60 sink.close();
61 });
62 jsonTest("$s1|$s2-$i-slice", expected, (sink) {
63 sink.addSlice(s.codeUnits, 0, i, false);
64 sink.addSlice(s.codeUnits, i, s.length, true);
65 });
66 for (var j = i; j < s.length - 1; j++) {
67 var s2a = s.substring(i, j);
68 var s2b = s.substring(j);
69 jsonTest("$s1|$s2a|$s2b-$i-$j", expected, (sink) {
70 sink.add(s1.codeUnits);
71 sink.add(s2a.codeUnits);
72 sink.add(s2b.codeUnits);
73 sink.close();
74 });
75 }
76 }
77 }
78
79 void testNumbers() {
80 for (var number in ["-0.12e-12", "-34.12E+12", "0.0e0", "9.9E9", "0", "9"
81 "1234.56789123456701418035663664340972900390625",
82 "1.2345678912345671e-14",
83 "99999999999999999999"]) {
84 var expected = num.parse(number);
85 for (int i = 1; i < number.length - 1; i++) {
86 var p1 = number.substring(0, i);
87 var p2 = number.substring(i);
88 jsonTest("$p1|$p2", expected, (sink) {
89 sink.add(p1.codeUnits);
90 sink.add(p2.codeUnits);
91 sink.close();
92 });
93
94 jsonTest("$p1|$p2/slice", expected, (sink) {
95 sink.addSlice(number.codeUnits, 0, i, false);
96 sink.addSlice(number.codeUnits, i, number.length, true);
97 });
98
99 for (int j = i; j < number.length - 1; j++) {
100 var p2a = number.substring(i, j);
101 var p2b = number.substring(j);
102 jsonTest("$p1|$p2a|$p2b", expected, (sink) {
103 sink.add(p1.codeUnits);
104 sink.add(p2a.codeUnits);
105 sink.add(p2b.codeUnits);
106 sink.close();
107 });
108 }
109 }
110 }
111 }
112
113 // Test that `null`, `true`, and `false` keywords are decoded correctly.
114 void testKeywords() {
115 for (var expected in [null, true, false]) {
116 var s = "$expected";
117 for (int i = 1; i < s.length - 1; i++) {
118 var s1 = s.substring(0, i);
119 var s2 = s.substring(i);
120 jsonTest("$s1|$s2", expected, (sink) {
121 sink.add(s1.codeUnits);
122 sink.add(s2.codeUnits);
123 sink.close();
124 });
125 jsonTest("$s1|$s2", expected, (sink) {
126 sink.addSlice(s.codeUnits, 0, i, false);
127 sink.addSlice(s.codeUnits, i, s.length, true);
128 });
129 for (var j = i; j < s.length - 1; j++) {
130 var s2a = s.substring(i, j);
131 var s2b = s.substring(j);
132 jsonTest("$s1|$s2a|$s2b", expected, (sink) {
133 sink.add(s1.codeUnits);
134 sink.add(s2a.codeUnits);
135 sink.add(s2b.codeUnits);
136 sink.close();
137 });
138 }
139 }
140 }
141 }
142
143 // Tests combinations of numbers, strings and keywords.
144 void testAll() {
145 var s = r'{"":[true,false,42, -33e-3,null,"\u0080"], "z": 0}';
146 bool check(o) {
147 if (o is Map) {
148 Expect.equals(2, o.length);
149 Expect.equals(0, o["z"]);
150 var v = o[""];
151 if (v is List) {
152 Expect.listEquals([true, false, 42, -33e-3, null, "\u0080"], v);
153 } else {
154 Expect.fail("Expected list, found ${v.runtimeType}");
155 }
156 } else {
157 Expect.fail("Expected map, found ${o.runtimeType}");
158 }
159 }
160 for (var i = 1; i < s.length - 1; i++) {
161 var s1 = s.substring(0, i);
162 var s2 = s.substring(i);
163 jsonParse("$s1|$s2-$i", check, (sink) {
164 sink.add(s1.codeUnits);
165 sink.add(s2.codeUnits);
166 sink.close();
167 });
168 jsonParse("$s1|$s2-$i-slice", check, (sink) {
169 sink.addSlice(s.codeUnits, 0, i, false);
170 sink.addSlice(s.codeUnits, i, s.length, true);
171 });
172 for (var j = i; j < s.length - 1; j++) {
173 var s2a = s.substring(i, j);
174 var s2b = s.substring(j);
175 jsonParse("$s1|$s2a|$s2b-$i-$j", check, (sink) {
176 sink.add(s1.codeUnits);
177 sink.add(s2a.codeUnits);
178 sink.add(s2b.codeUnits);
179 sink.close();
180 });
181 }
182 }
183
184 }
185
186 // Check that [codes] decode to [expect] when allowing malformed UTF-8,
187 // and throws otherwise.
188 void jsonMalformedTest(name, expect, codes) {
189 // Helper method.
190 void test(name, expect, action(sink)) {
191 var tag = "Malform:$name-$expect";
192 { // Allowing malformed, expect [expect]
193 var sink = new ChunkedConversionSink.withCallback((values) {
194 var value = values[0];
195 Expect.equals(expect, value, tag);
196 });
197 var decoderSink = JSON.decoder.startChunkedConversion(sink)
198 .asUtf8Sink(true);
199 try {
200 action(decoderSink);
201 } catch (e, s) {
202 Expect.fail("Unexpected throw ($tag): $e\n$s");
203 }
204 }
205 { // Not allowing malformed, expect throw.
206 var sink = new ChunkedConversionSink.withCallback((values) {
207 Expect.unreachable(tag);
208 });
209 var decoderSink = JSON.decoder.startChunkedConversion(sink)
210 .asUtf8Sink(false);
211 Expect.throws(() { action(decoderSink); }, null, tag);
212 }
213 }
214
215 // Test all two and three part slices.
216 for (int i = 1; i < codes.length - 1; i++) {
217 test("$name:$i", expect, (sink) {
218 sink.add(codes.sublist(0, i));
219 sink.add(codes.sublist(i));
220 sink.close();
221 });
222 test("$name:$i-slice", expect, (sink) {
223 sink.addSlice(codes, 0, i, false);
224 sink.addSlice(codes, i, codes.length, true);
225 });
226 for (int j = i; j < codes.length - 1; j++) {
227 test("$name:$i|$j", expect, (sink) {
228 sink.add(codes.sublist(0, i));
229 sink.add(codes.sublist(i, j));
230 sink.add(codes.sublist(j));
231 sink.close();
232 });
233 }
234 }
235 }
236
237 // Test that `codeString.codeUnits` fails to parse as UTF-8 JSON,
238 // even with decoder not throwing on malformed encodings.
239 void jsonThrows(Strig name, String codeString) {
240 testJsonThrows(tag, action) {
241 // Not allowing malformed, expect throw.
242 var sink = new ChunkedConversionSink.withCallback((values) {
243 Expect.unreachable(tag);
244 });
245 var decoderSink = JSON.decoder.startChunkedConversion(sink)
246 .asUtf8Sink(true);
247 Expect.throws(() { action(decoderSink); }, null, tag);
248 }
249
250 var codes = codeString.codeUnits;
251 for (int i = 1; i < codes.length - 1; i++) {
252 testJsonThrows("$name:$i", (sink) {
253 sink.add(codes.sublist(0, i));
254 sink.add(codes.sublist(i));
255 sink.close();
256 });
257 testJsonThrows("$name:$i-slice", (sink) {
258 sink.addSlice(codes, 0, i, false);
259 sink.addSlice(codes, i, codes.length, true);
260 });
261 for (int j = i; j < codes.length - 1; j++) {
262 testJsonThrows("$name:$i|$j", (sink) {
263 sink.add(codes.sublist(0, i));
264 sink.add(codes.sublist(i, j));
265 sink.add(codes.sublist(j));
266 sink.close();
267 });
268 }
269 }
270 }
271
272 // Malformed UTF-8 encodings.
273 void testMalformed() {
274 // Overlong encodings.
275 jsonMalformedTest("overlong-0-2", "@\uFFFD@",
276 [0x22, 0x40, 0xc0, 0x80, 0x40, 0x22]);
277 jsonMalformedTest("overlong-0-3", "@\uFFFD@",
278 [0x22, 0x40, 0xe0, 0x80, 0x80, 0x40, 0x22]);
279 jsonMalformedTest("overlong-0-4", "@\uFFFD@",
280 [0x22, 0x40, 0xf0, 0x80, 0x80, 0x80, 0x40, 0x22]);
281
282 jsonMalformedTest("overlong-7f-2", "@\uFFFD@",
283 [0x22, 0x40, 0xc1, 0xbf, 0x40, 0x22]);
284 jsonMalformedTest("overlong-7f-3", "@\uFFFD@",
285 [0x22, 0x40, 0xe0, 0x81, 0xbf, 0x40, 0x22]);
286 jsonMalformedTest("overlong-7f-4", "@\uFFFD@",
287 [0x22, 0x40, 0xf0, 0x80, 0x81, 0xbf, 0x40, 0x22]);
288
289 jsonMalformedTest("overlong-80-3", "@\uFFFD@",
290 [0x22, 0x40, 0xe0, 0x82, 0x80, 0x40, 0x22]);
291 jsonMalformedTest("overlong-80-4", "@\uFFFD@",
292 [0x22, 0x40, 0xf0, 0x80, 0x82, 0x80, 0x40, 0x22]);
293
294 jsonMalformedTest("overlong-7ff-3", "@\uFFFD@",
295 [0x22, 0x40, 0xe0, 0x9f, 0xbf, 0x40, 0x22]);
296 jsonMalformedTest("overlong-7ff-4", "@\uFFFD@",
297 [0x22, 0x40, 0xf0, 0x80, 0x9f, 0xbf, 0x40, 0x22]);
298
299 jsonMalformedTest("overlong-800-4", "@\uFFFD@",
300 [0x22, 0x40, 0xf0, 0x80, 0xa0, 0x80, 0x40, 0x22]);
301 jsonMalformedTest("overlong-ffff-4", "@\uFFFD@",
302 [0x22, 0x40, 0xf0, 0x8f, 0xbf, 0xbf, 0x40, 0x22]);
303
304 // Unterminated multibyte sequences.
305 jsonMalformedTest("unterminated-2-normal", "@\uFFFD@",
306 [0x22, 0x40, 0xc0, 0x40, 0x22]);
307
308 jsonMalformedTest("unterminated-3-normal", "@\uFFFD@",
309 [0x22, 0x40, 0xe0, 0x80, 0x40, 0x22]);
310
311 jsonMalformedTest("unterminated-4-normal", "@\uFFFD@",
312 [0x22, 0x40, 0xf0, 0x80, 0x80, 0x40, 0x22]);
313
314 jsonMalformedTest("unterminated-2-multi", "@\uFFFD\x80@",
315 [0x22, 0x40, 0xc0, 0xc2, 0x80, 0x40, 0x22]);
316
317 jsonMalformedTest("unterminated-3-multi", "@\uFFFD\x80@",
318 [0x22, 0x40, 0xe0, 0x80, 0xc2, 0x80, 0x40, 0x22]);
319
320 jsonMalformedTest("unterminated-4-multi", "@\uFFFD\x80@",
321 [0x22, 0x40, 0xf0, 0x80, 0x80, 0xc2, 0x80, 0x40, 0x22]);
322
323 jsonMalformedTest("unterminated-2-escape", "@\uFFFD\n@",
324 [0x22, 0x40, 0xc0, 0x5c, 0x6e, 0x40, 0x22]);
325
326 jsonMalformedTest("unterminated-3-escape", "@\uFFFD\n@",
327 [0x22, 0x40, 0xe0, 0x80, 0x5c, 0x6e, 0x40, 0x22]);
328
329 jsonMalformedTest("unterminated-4-escape", "@\uFFFD\n@",
330 [0x22, 0x40, 0xf0, 0x80, 0x80, 0x5c, 0x6e, 0x40, 0x22]);
331
332 jsonMalformedTest("unterminated-2-end", "@\uFFFD",
333 [0x22, 0x40, 0xc0, 0x22]);
334
335 jsonMalformedTest("unterminated-3-end", "@\uFFFD",
336 [0x22, 0x40, 0xe0, 0x80, 0x22]);
337
338 jsonMalformedTest("unterminated-4-end", "@\uFFFD",
339 [0x22, 0x40, 0xf0, 0x80, 0x80, 0x22]);
340
341 // Unexpected continuation byte
342 // - after a normal character.
343 jsonMalformedTest("continuation-normal", "@\uFFFD@",
344 [0x22, 0x40, 0x80, 0x40, 0x22]);
345
346 // - after a valid continuation byte.
347 jsonMalformedTest("continuation-continuation-2", "@\x80\uFFFD@",
348 [0x22, 0x40, 0xc2, 0x80, 0x80, 0x40, 0x22]);
349 jsonMalformedTest("continuation-continuation-3", "@\u0800\uFFFD@",
350 [0x22, 0x40, 0xe0, 0xa0, 0x80, 0x80, 0x40, 0x22]);
351 jsonMalformedTest("continuation-continuation-4", "@\u{10000}\uFFFD@",
352 [0x22, 0x40, 0xf0, 0x90, 0x80, 0x80, 0x80, 0x40, 0x22]);
353
354 // - after another invalid continuation byte
355 jsonMalformedTest("continuation-twice", "@\uFFFD\uFFFD\uFFFD@",
356 [0x22, 0x40, 0x80, 0x80, 0x80, 0x40, 0x22]);
357 // - at start.
358 jsonMalformedTest("continuation-start", "\uFFFD@",
359 [0x22, 0x80, 0x40, 0x22]);
360
361 // Unexpected leading byte where continuation byte expected.
362 jsonMalformedTest("leading-2", "@\uFFFD\x80@",
363 [0x22, 0x40, 0xc0, 0xc2, 0x80, 0x40, 0x22]);
364 jsonMalformedTest("leading-3-1", "@\uFFFD\x80@",
365 [0x22, 0x40, 0xe0, 0xc2, 0x80, 0x40, 0x22]);
366 jsonMalformedTest("leading-3-2", "@\uFFFD\x80@",
367 [0x22, 0x40, 0xe0, 0x80, 0xc2, 0x80, 0x40, 0x22]);
368 jsonMalformedTest("leading-4-1", "@\uFFFD\x80@",
369 [0x22, 0x40, 0xf0, 0xc2, 0x80, 0x40, 0x22]);
370 jsonMalformedTest("leading-4-2", "@\uFFFD\x80@",
371 [0x22, 0x40, 0xf0, 0x80, 0xc2, 0x80, 0x40, 0x22]);
372 jsonMalformedTest("leading-4-3", "@\uFFFD\x80@",
373 [0x22, 0x40, 0xf0, 0x80, 0x80, 0xc2, 0x80, 0x40, 0x22]);
374
375 // Overlong encodings of ASCII outside of strings always fail.
376 // Use Latin-1 strings as argument since most chars are correct,
377 // pass string.codeUnits to decoder as UTF-8.
378 jsonThrows("number-1", "\xc0\xab0.0e-0"); // '-' is 0x2b => \xc0\xab
379 jsonThrows("number-2", "-\xc0\xb0.0e-0"); // '0' is 0x30 => \xc0\xb0
380 jsonThrows("number-3", "-0\xc0\xae0e-0"); // '.' is 0x2e => \xc0\xae
381 jsonThrows("number-4", "-0.\xc0\xb0e-0");
382 jsonThrows("number-5", "-0.0\xc1\xa5-0"); // 'e' is 0x65 => \xc1\xa5
383 jsonThrows("number-6", "-0.0e\xc0\xab0");
384 jsonThrows("number-7", "-0.0e-\xc0\xb0");
385
386 jsonThrows("true-1", "\xc1\xb4rue"); // 't' is 0x74
387 jsonThrows("true-2", "t\xc1\xb2ue"); // 'r' is 0x72
388 jsonThrows("true-3", "tr\xc1\xb5e"); // 'u' is 0x75
389 jsonThrows("true-4", "tru\xc1\xa5"); // 'e' is 0x65
390
391 jsonThrows("false-1", "\xc1\xa6alse"); // 'f' is 0x66
392 jsonThrows("false-2", "f\xc1\xa1lse"); // 'a' is 0x61
393 jsonThrows("false-3", "fa\xc1\xacse"); // 'l' is 0x6c
394 jsonThrows("false-4", "fal\xc1\xb3e"); // 's' is 0x73
395 jsonThrows("false-5", "fals\xc1\xa5"); // 'e' is 0x65
396
397 jsonThrows("null-1", "\xc1\xaeull"); // 'n' is 0x6e
398 jsonThrows("null-2", "n\xc1\xb5ll"); // 'u' is 0x75
399 jsonThrows("null-3", "nu\xc1\xacl"); // 'l' is 0x6c
400 jsonThrows("null-4", "nul\xc1\xac"); // 'l' is 0x6c
401
402 jsonThrows("array-1", "\xc1\x9b0,0]"); // '[' is 0x5b
403 jsonThrows("array-2", "[0,0\xc1\x9d"); // ']' is 0x5d
404 jsonThrows("array-2", "[0\xc0\xac0]"); // ',' is 0x2c
405
406 jsonThrows("object-1", '\xc1\xbb"x":0}'); // '{' is 0x7b
407 jsonThrows("object-2", '{"x":0\xc1\xbd'); // '}' is 0x7d
408 jsonThrows("object-2", '{"x\xc0\xba0}'); // ':' is 0x3a
409
410 jsonThrows("string-1", '\xc0\xa2x"'); // '"' is 0x22
411 jsonThrows("string-1", '"x\xc0\xa2'); // Unterminated string.
412
413 jsonThrows("whitespace-1", "\xc0\xa01"); // ' ' is 0x20
414 }
415
416 void testUnicodeTests() {
417 for (var pair in UNICODE_TESTS) {
418 var bytes = pair[0];
419 var string = pair[1];
420 int step = 1;
421 if (bytes.length > 100) step = bytes.length ~/ 13;
422 for (int i = 1; i < bytes.length - 1; i += step) {
423 jsonTest("$string:$i", string, (sink) {
424 sink.add([0x22]); // Double-quote.
425 sink.add(bytes.sublist(0, i));
426 sink.add(bytes.sublist(i));
427 sink.add([0x22]);
428 sink.close();
429 });
430 jsonTest("$string:$i-slice", string, (sink) {
431 sink.addSlice([0x22], 0, 1, false);
432 sink.addSlice(bytes, 0, i, false);
433 sink.addSlice(bytes, i, bytes.length, false);
434 sink.addSlice([0x22], 0, 1, true);
435 });
436 int skip = 1;
437 if (bytes.length > 25) skip = bytes.length ~/ 17;
438 for (int j = i; j < bytes.length - 1; j += skip) {
439 jsonTest("$string:$i|$j", string, (sink) {
440 sink.add([0x22]);
441 sink.add(bytes.sublist(0, i));
442 sink.add(bytes.sublist(i, j));
443 sink.add(bytes.sublist(j));
444 sink.add([0x22]);
445 sink.close();
446 });
447 }
448 }
449 }
450 }
OLDNEW
« no previous file with comments | « tests/lib/convert/json_test.dart ('k') | tests/lib/convert/unicode_tests.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698