| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 // Disable background compilation so that the Issue 24908 can be reproduced. | 4 // Disable background compilation so that the Issue 24908 can be reproduced. |
| 5 // VMOptions=--no-background-compilation | 5 // VMOptions=--no-background-compilation |
| 6 | 6 |
| 7 library json_test; | 7 library json_test; |
| 8 | 8 |
| 9 import "package:expect/expect.dart"; | 9 import "package:expect/expect.dart"; |
| 10 import "dart:convert"; | 10 import "dart:convert"; |
| (...skipping 11 matching lines...) Expand all Loading... |
| 22 } else if (expected is Map) { | 22 } else if (expected is Map) { |
| 23 Expect.isTrue(actual is Map); | 23 Expect.isTrue(actual is Map); |
| 24 Expect.equals(expected.length, actual.length, "$path: Map size"); | 24 Expect.equals(expected.length, actual.length, "$path: Map size"); |
| 25 expected.forEach((key, value) { | 25 expected.forEach((key, value) { |
| 26 Expect.isTrue(actual.containsKey(key)); | 26 Expect.isTrue(actual.containsKey(key)); |
| 27 compare(value, actual[key], "$path[$key]"); | 27 compare(value, actual[key], "$path[$key]"); |
| 28 }); | 28 }); |
| 29 } else if (expected is num) { | 29 } else if (expected is num) { |
| 30 Expect.equals(expected is int, actual is int, "$path: same number type"); | 30 Expect.equals(expected is int, actual is int, "$path: same number type"); |
| 31 Expect.isTrue(expected.compareTo(actual) == 0, | 31 Expect.isTrue(expected.compareTo(actual) == 0, |
| 32 "$path: Expected: $expected, was: $actual"); | 32 "$path: Expected: $expected, was: $actual"); |
| 33 } else { | 33 } else { |
| 34 // String, bool, null. | 34 // String, bool, null. |
| 35 Expect.equals(expected, actual, path); | 35 Expect.equals(expected, actual, path); |
| 36 } | 36 } |
| 37 } | 37 } |
| 38 |
| 38 for (var reviver in [null, (k, v) => v]) { | 39 for (var reviver in [null, (k, v) => v]) { |
| 39 for (var split in [0, 1, 2, 3]) { | 40 for (var split in [0, 1, 2, 3]) { |
| 40 var name = (reviver == null) ? "" : "reviver:"; | 41 var name = (reviver == null) ? "" : "reviver:"; |
| 41 var sink = new ChunkedConversionSink.withCallback((values) { | 42 var sink = new ChunkedConversionSink.withCallback((values) { |
| 42 var value = values[0]; | 43 var value = values[0]; |
| 43 compare(expected, value, "$name$value"); | 44 compare(expected, value, "$name$value"); |
| 44 }); | 45 }); |
| 45 var decoderSink = JSON.decoder.startChunkedConversion(sink); | 46 var decoderSink = JSON.decoder.startChunkedConversion(sink); |
| 46 switch (split) { | 47 switch (split) { |
| 47 case 0: | 48 case 0: |
| (...skipping 27 matching lines...) Expand all Loading... |
| 75 break; | 76 break; |
| 76 } | 77 } |
| 77 } | 78 } |
| 78 } | 79 } |
| 79 } | 80 } |
| 80 | 81 |
| 81 String escape(String s) { | 82 String escape(String s) { |
| 82 var sb = new StringBuffer(); | 83 var sb = new StringBuffer(); |
| 83 for (int i = 0; i < s.length; i++) { | 84 for (int i = 0; i < s.length; i++) { |
| 84 int code = s.codeUnitAt(i); | 85 int code = s.codeUnitAt(i); |
| 85 if (code == '\\'.codeUnitAt(0)) sb.write(r'\\'); | 86 if (code == '\\'.codeUnitAt(0)) |
| 86 else if (code == '\"'.codeUnitAt(0)) sb.write(r'\"'); | 87 sb.write(r'\\'); |
| 87 else if (code >= 32 && code < 127) sb.writeCharCode(code); | 88 else if (code == '\"'.codeUnitAt(0)) |
| 89 sb.write(r'\"'); |
| 90 else if (code >= 32 && code < 127) |
| 91 sb.writeCharCode(code); |
| 88 else { | 92 else { |
| 89 String hex = '000${code.toRadixString(16)}'; | 93 String hex = '000${code.toRadixString(16)}'; |
| 90 sb.write(r'\u' '${hex.substring(hex.length - 4)}'); | 94 sb.write(r'\u' '${hex.substring(hex.length - 4)}'); |
| 91 } | 95 } |
| 92 } | 96 } |
| 93 return '$sb'; | 97 return '$sb'; |
| 94 } | 98 } |
| 95 | 99 |
| 96 void testThrows(json) { | 100 void testThrows(json) { |
| 97 Expect.throws(() => JSON.decode(json), badFormat, "json = '${escape(json)}'"); | 101 Expect.throws(() => JSON.decode(json), badFormat, "json = '${escape(json)}'"); |
| 98 } | 102 } |
| 99 | 103 |
| 100 testNumbers() { | 104 testNumbers() { |
| 101 // Positive tests for number formats. | 105 // Positive tests for number formats. |
| 102 var integerList = ["0","9","9999"]; | 106 var integerList = ["0", "9", "9999"]; |
| 103 var signList = ["", "-"]; | 107 var signList = ["", "-"]; |
| 104 var fractionList = ["", ".0", ".1", ".99999"]; | 108 var fractionList = ["", ".0", ".1", ".99999"]; |
| 105 var exponentList = [""]; | 109 var exponentList = [""]; |
| 106 for (var exphead in ["e", "E", "e-", "E-", "e+", "E+"]) { | 110 for (var exphead in ["e", "E", "e-", "E-", "e+", "E+"]) { |
| 107 for (var expval in ["0", "1", "200"]) { | 111 for (var expval in ["0", "1", "200"]) { |
| 108 exponentList.add("$exphead$expval"); | 112 exponentList.add("$exphead$expval"); |
| 109 } | 113 } |
| 110 } | 114 } |
| 111 | 115 |
| 112 for (var integer in integerList) { | 116 for (var integer in integerList) { |
| (...skipping 12 matching lines...) Expand all Loading... |
| 125 | 129 |
| 126 // Negative tests (syntax error). | 130 // Negative tests (syntax error). |
| 127 // testError thoroughly tests the given parts with a lot of valid | 131 // testError thoroughly tests the given parts with a lot of valid |
| 128 // values for the other parts. | 132 // values for the other parts. |
| 129 testError({signs, integers, fractions, exponents}) { | 133 testError({signs, integers, fractions, exponents}) { |
| 130 def(value, defaultValue) { | 134 def(value, defaultValue) { |
| 131 if (value == null) return defaultValue; | 135 if (value == null) return defaultValue; |
| 132 if (value is List) return value; | 136 if (value is List) return value; |
| 133 return [value]; | 137 return [value]; |
| 134 } | 138 } |
| 139 |
| 135 signs = def(signs, signList); | 140 signs = def(signs, signList); |
| 136 integers = def(integers, integerList); | 141 integers = def(integers, integerList); |
| 137 fractions = def(fractions, fractionList); | 142 fractions = def(fractions, fractionList); |
| 138 exponents = def(exponents, exponentList); | 143 exponents = def(exponents, exponentList); |
| 139 for (var integer in integers) { | 144 for (var integer in integers) { |
| 140 for (var sign in signs) { | 145 for (var sign in signs) { |
| 141 for (var fraction in fractions) { | 146 for (var fraction in fractions) { |
| 142 for (var exponent in exponents) { | 147 for (var exponent in exponents) { |
| 143 var literal = "$sign$integer$fraction$exponent"; | 148 var literal = "$sign$integer$fraction$exponent"; |
| 144 testThrows(literal); | 149 testThrows(literal); |
| 145 } | 150 } |
| 146 } | 151 } |
| 147 } | 152 } |
| 148 } | 153 } |
| 149 } | 154 } |
| 155 |
| 150 // Doubles overflow to Infinity. | 156 // Doubles overflow to Infinity. |
| 151 testJson("1e+400", double.INFINITY); | 157 testJson("1e+400", double.INFINITY); |
| 152 // (Integers do not, but we don't have those on dart2js). | 158 // (Integers do not, but we don't have those on dart2js). |
| 153 | 159 |
| 154 // Integer part cannot be omitted: | 160 // Integer part cannot be omitted: |
| 155 testError(integers: ""); | 161 testError(integers: ""); |
| 156 | 162 |
| 157 // Test for "Initial zero only allowed for zero integer part" moved to | 163 // Test for "Initial zero only allowed for zero integer part" moved to |
| 158 // json_strict_test.dart because IE's JSON.decode accepts additional initial | 164 // json_strict_test.dart because IE's JSON.decode accepts additional initial |
| 159 // zeros. | 165 // zeros. |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 208 testJson('"........\\$esc"', "........$lit"); | 214 testJson('"........\\$esc"', "........$lit"); |
| 209 // Escape middle. | 215 // Escape middle. |
| 210 testJson('"....\\$esc...."', "....$lit...."); | 216 testJson('"....\\$esc...."', "....$lit...."); |
| 211 }); | 217 }); |
| 212 | 218 |
| 213 // Does not accept single quotes. | 219 // Does not accept single quotes. |
| 214 testThrows(r"''"); | 220 testThrows(r"''"); |
| 215 // Throws on unterminated strings. | 221 // Throws on unterminated strings. |
| 216 testThrows(r'"......\"'); | 222 testThrows(r'"......\"'); |
| 217 // Throws on unterminated escapes. | 223 // Throws on unterminated escapes. |
| 218 testThrows(r'"\'); // ' is not escaped. | 224 testThrows(r'"\'); // ' is not escaped. |
| 219 testThrows(r'"\a"'); | 225 testThrows(r'"\a"'); |
| 220 testThrows(r'"\u"'); | 226 testThrows(r'"\u"'); |
| 221 testThrows(r'"\u1"'); | 227 testThrows(r'"\u1"'); |
| 222 testThrows(r'"\u12"'); | 228 testThrows(r'"\u12"'); |
| 223 testThrows(r'"\u123"'); | 229 testThrows(r'"\u123"'); |
| 224 testThrows(r'"\ux"'); | 230 testThrows(r'"\ux"'); |
| 225 testThrows(r'"\u1x"'); | 231 testThrows(r'"\u1x"'); |
| 226 testThrows(r'"\u12x"'); | 232 testThrows(r'"\u12x"'); |
| 227 testThrows(r'"\u123x"'); | 233 testThrows(r'"\u123x"'); |
| 228 // Throws on bad escapes. | 234 // Throws on bad escapes. |
| 229 testThrows(r'"\a"'); | 235 testThrows(r'"\a"'); |
| 230 testThrows(r'"\x00"'); | 236 testThrows(r'"\x00"'); |
| 231 testThrows(r'"\c2"'); | 237 testThrows(r'"\c2"'); |
| 232 testThrows(r'"\000"'); | 238 testThrows(r'"\000"'); |
| 233 testThrows(r'"\u{0}"'); | 239 testThrows(r'"\u{0}"'); |
| 234 testThrows(r'"\%"'); | 240 testThrows(r'"\%"'); |
| 235 testThrows('"\\\x00"'); // Not raw string! | 241 testThrows('"\\\x00"'); // Not raw string! |
| 236 // Throws on control characters. | 242 // Throws on control characters. |
| 237 for (int i = 0; i < 32; i++) { | 243 for (int i = 0; i < 32; i++) { |
| 238 var string = new String.fromCharCodes([0x22,i,0x22]); // '"\x00"' etc. | 244 var string = new String.fromCharCodes([0x22, i, 0x22]); // '"\x00"' etc. |
| 239 testThrows(string); | 245 testThrows(string); |
| 240 } | 246 } |
| 241 } | 247 } |
| 242 | 248 |
| 243 | |
| 244 testObjects() { | 249 testObjects() { |
| 245 testJson(r'{}', {}); | 250 testJson(r'{}', {}); |
| 246 testJson(r'{"x":42}', {"x":42}); | 251 testJson(r'{"x":42}', {"x": 42}); |
| 247 testJson(r'{"x":{"x":{"x":42}}}', {"x": {"x": {"x": 42}}}); | 252 testJson(r'{"x":{"x":{"x":42}}}', { |
| 253 "x": { |
| 254 "x": {"x": 42} |
| 255 } |
| 256 }); |
| 248 testJson(r'{"x":10,"x":42}', {"x": 42}); | 257 testJson(r'{"x":10,"x":42}', {"x": 42}); |
| 249 testJson(r'{"":42}', {"": 42}); | 258 testJson(r'{"":42}', {"": 42}); |
| 250 | 259 |
| 251 // Keys must be strings. | 260 // Keys must be strings. |
| 252 testThrows(r'{x:10}'); | 261 testThrows(r'{x:10}'); |
| 253 testThrows(r'{true:10}'); | 262 testThrows(r'{true:10}'); |
| 254 testThrows(r'{false:10}'); | 263 testThrows(r'{false:10}'); |
| 255 testThrows(r'{null:10}'); | 264 testThrows(r'{null:10}'); |
| 256 testThrows(r'{42:10}'); | 265 testThrows(r'{42:10}'); |
| 257 testThrows(r'{42e1:10}'); | 266 testThrows(r'{42e1:10}'); |
| 258 testThrows(r'{-42:10}'); | 267 testThrows(r'{-42:10}'); |
| 259 testThrows(r'{["text"]:10}'); | 268 testThrows(r'{["text"]:10}'); |
| 260 testThrows(r'{:10}'); | 269 testThrows(r'{:10}'); |
| 261 } | 270 } |
| 262 | 271 |
| 263 testArrays() { | 272 testArrays() { |
| 264 testJson(r'[]', []); | 273 testJson(r'[]', []); |
| 265 testJson(r'[1.1e1,"string",true,false,null,{}]', | 274 testJson(r'[1.1e1,"string",true,false,null,{}]', |
| 266 [1.1e1, "string", true, false, null, {}]); | 275 [1.1e1, "string", true, false, null, {}]); |
| 267 testJson(r'[[[[[[]]]],[[[]]],[[]]]]', [[[[[[]]]],[[[]]],[[]]]]); | 276 testJson(r'[[[[[[]]]],[[[]]],[[]]]]', [ |
| 268 testJson(r'[{},[{}],{"x":[]}]', [{},[{}],{"x":[]}]); | 277 [ |
| 278 [ |
| 279 [ |
| 280 [[]] |
| 281 ] |
| 282 ], |
| 283 [ |
| 284 [[]] |
| 285 ], |
| 286 [[]] |
| 287 ] |
| 288 ]); |
| 289 testJson(r'[{},[{}],{"x":[]}]', [ |
| 290 {}, |
| 291 [{}], |
| 292 {"x": []} |
| 293 ]); |
| 269 | 294 |
| 270 testThrows(r'[1,,2]'); | 295 testThrows(r'[1,,2]'); |
| 271 testThrows(r'[1,2,]'); | 296 testThrows(r'[1,2,]'); |
| 272 testThrows(r'[,2]'); | 297 testThrows(r'[,2]'); |
| 273 } | 298 } |
| 274 | 299 |
| 275 testWords() { | 300 testWords() { |
| 276 testJson(r'true', true); | 301 testJson(r'true', true); |
| 277 testJson(r'false', false); | 302 testJson(r'false', false); |
| 278 testJson(r'null', null); | 303 testJson(r'null', null); |
| 279 testJson(r'[true]', [true]); | 304 testJson(r'[true]', [true]); |
| 280 testJson(r'{"true":true}', {"true": true}); | 305 testJson(r'{"true":true}', {"true": true}); |
| 281 | 306 |
| 282 testThrows(r'truefalse'); | 307 testThrows(r'truefalse'); |
| 283 testThrows(r'trues'); | 308 testThrows(r'trues'); |
| 284 testThrows(r'nulll'); | 309 testThrows(r'nulll'); |
| 285 testThrows(r'full'); | 310 testThrows(r'full'); |
| 286 testThrows(r'nul'); | 311 testThrows(r'nul'); |
| 287 testThrows(r'tru'); | 312 testThrows(r'tru'); |
| 288 testThrows(r'fals'); | 313 testThrows(r'fals'); |
| 289 testThrows(r'\null'); | 314 testThrows(r'\null'); |
| 290 testThrows(r't\rue'); | 315 testThrows(r't\rue'); |
| 291 testThrows(r't\rue'); | 316 testThrows(r't\rue'); |
| 292 } | 317 } |
| 293 | 318 |
| 294 testWhitespace() { | 319 testWhitespace() { |
| 295 // Valid white-space characters. | 320 // Valid white-space characters. |
| 296 var v = '\t\r\n\ '; | 321 var v = '\t\r\n\ '; |
| 297 // Invalid white-space and non-recognized characters. | 322 // Invalid white-space and non-recognized characters. |
| 298 var invalids = ['\x00', '\f', '\x08', '\\', '\xa0','\u2028', '\u2029']; | 323 var invalids = ['\x00', '\f', '\x08', '\\', '\xa0', '\u2028', '\u2029']; |
| 299 | 324 |
| 300 // Valid whitespace accepted "everywhere". | 325 // Valid whitespace accepted "everywhere". |
| 301 testJson('$v[${v}-2.2e2$v,$v{$v"key"$v:${v}true$v}$v,$v"ab"$v]$v', | 326 testJson('$v[${v}-2.2e2$v,$v{$v"key"$v:${v}true$v}$v,$v"ab"$v]$v', [ |
| 302 [-2.2e2, {"key": true}, "ab"]); | 327 -2.2e2, |
| 328 {"key": true}, |
| 329 "ab" |
| 330 ]); |
| 303 | 331 |
| 304 // IE9 accepts invalid characters at the end, so some of these tests have been | 332 // IE9 accepts invalid characters at the end, so some of these tests have been |
| 305 // moved to json_strict_test.dart. | 333 // moved to json_strict_test.dart. |
| 306 for (var i in invalids) { | 334 for (var i in invalids) { |
| 307 testThrows('${i}"s"'); | 335 testThrows('${i}"s"'); |
| 308 testThrows('42${i}'); | 336 testThrows('42${i}'); |
| 309 testThrows('$i[]'); | 337 testThrows('$i[]'); |
| 310 testThrows('[$i]'); | 338 testThrows('[$i]'); |
| 311 testThrows('[$i"s"]'); | 339 testThrows('[$i"s"]'); |
| 312 testThrows('["s"$i]'); | 340 testThrows('["s"$i]'); |
| 313 testThrows('$i{"k":"v"}'); | 341 testThrows('$i{"k":"v"}'); |
| 314 testThrows('{$i"k":"v"}'); | 342 testThrows('{$i"k":"v"}'); |
| 315 testThrows('{"k"$i:"v"}'); | 343 testThrows('{"k"$i:"v"}'); |
| 316 testThrows('{"k":$i"v"}'); | 344 testThrows('{"k":$i"v"}'); |
| 317 testThrows('{"k":"v"$i}'); | 345 testThrows('{"k":"v"$i}'); |
| 318 } | 346 } |
| 319 } | 347 } |
| 320 | 348 |
| 321 main() { | 349 main() { |
| 322 testNumbers(); | 350 testNumbers(); |
| 323 testStrings(); | 351 testStrings(); |
| 324 testWords(); | 352 testWords(); |
| 325 testObjects(); | 353 testObjects(); |
| 326 testArrays(); | 354 testArrays(); |
| 327 testWhitespace(); | 355 testWhitespace(); |
| 328 } | 356 } |
| OLD | NEW |