| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 library json_test; | |
| 6 | |
| 7 import "package:expect/expect.dart"; | |
| 8 import "dart:convert"; | |
| 9 | |
| 10 bool badFormat(e) => e is FormatException; | |
| 11 | |
| 12 void testJson(json, expected) { | |
| 13 var value = JSON.decode(json); | |
| 14 compare(expected, actual, path) { | |
| 15 if (expected is List) { | |
| 16 Expect.isTrue(actual is List); | |
| 17 Expect.equals(expected.length, actual.length, "$path: List length"); | |
| 18 for (int i = 0; i < expected.length; i++) { | |
| 19 compare(expected[i], actual[i], "$path[$i]"); | |
| 20 } | |
| 21 } else if (expected is Map) { | |
| 22 Expect.isTrue(actual is Map); | |
| 23 Expect.equals(expected.length, actual.length, "$path: Map size"); | |
| 24 expected.forEach((key, value) { | |
| 25 Expect.isTrue(actual.containsKey(key)); | |
| 26 compare(value, actual[key], "$path[$key]"); | |
| 27 }); | |
| 28 } else if (expected is num) { | |
| 29 Expect.equals(expected is int, actual is int, "$path: same number type"); | |
| 30 Expect.isTrue(expected.compareTo(actual) == 0, | |
| 31 "$path: $expected vs. $actual"); | |
| 32 } else { | |
| 33 // String, bool, null. | |
| 34 Expect.equals(expected, actual, path); | |
| 35 } | |
| 36 } | |
| 37 compare(expected, value, "value"); | |
| 38 } | |
| 39 | |
| 40 String escape(String s) { | |
| 41 var sb = new StringBuffer(); | |
| 42 for (int i = 0; i < s.length; i++) { | |
| 43 int code = s.codeUnitAt(i); | |
| 44 if (code == '\\'.codeUnitAt(0)) sb.write(r'\\'); | |
| 45 else if (code == '\"'.codeUnitAt(0)) sb.write(r'\"'); | |
| 46 else if (code >= 32 && code < 127) sb.writeCharCode(code); | |
| 47 else { | |
| 48 String hex = '000${code.toRadixString(16)}'; | |
| 49 sb.write(r'\u' '${hex.substring(hex.length - 4)}'); | |
| 50 } | |
| 51 } | |
| 52 return '$sb'; | |
| 53 } | |
| 54 | |
| 55 void testThrows(json) { | |
| 56 Expect.throws(() => JSON.decode(json), badFormat, "json = '${escape(json)}'"); | |
| 57 } | |
| 58 | |
| 59 testNumbers() { | |
| 60 // Positive tests for number formats. | |
| 61 var integerList = ["0","9","9999"]; | |
| 62 var signList = ["", "-"]; | |
| 63 var fractionList = ["", ".0", ".1", ".99999"]; | |
| 64 var exponentList = [""]; | |
| 65 for (var exphead in ["e", "E", "e-", "E-", "e+", "E+"]) { | |
| 66 for (var expval in ["0", "1", "200"]) { | |
| 67 exponentList.add("$exphead$expval"); | |
| 68 } | |
| 69 } | |
| 70 | |
| 71 for (var integer in integerList) { | |
| 72 for (var sign in signList) { | |
| 73 for (var fraction in fractionList) { | |
| 74 for (var exp in exponentList) { | |
| 75 var literal = "$sign$integer$fraction$exp"; | |
| 76 var parseNumber = | |
| 77 ((fraction == "" && exp == "") ? (String x) => int.parse(x) | |
| 78 : (String x) => double.parse(x)); | |
| 79 var expectedValue = parseNumber(literal); | |
| 80 testJson(literal, expectedValue); | |
| 81 } | |
| 82 } | |
| 83 } | |
| 84 } | |
| 85 | |
| 86 // Negative tests (syntax error). | |
| 87 // testError thoroughly tests the given parts with a lot of valid | |
| 88 // values for the other parts. | |
| 89 testError({signs, integers, fractions, exponents}) { | |
| 90 def(value, defaultValue) { | |
| 91 if (value == null) return defaultValue; | |
| 92 if (value is List) return value; | |
| 93 return [value]; | |
| 94 } | |
| 95 signs = def(signs, signList); | |
| 96 integers = def(integers, integerList); | |
| 97 fractions = def(fractions, fractionList); | |
| 98 exponents = def(exponents, exponentList); | |
| 99 for (var integer in integers) { | |
| 100 for (var sign in signs) { | |
| 101 for (var fraction in fractions) { | |
| 102 for (var exponent in exponents) { | |
| 103 var literal = "$sign$integer$fraction$exponent"; | |
| 104 testThrows(literal); | |
| 105 } | |
| 106 } | |
| 107 } | |
| 108 } | |
| 109 } | |
| 110 // Doubles overflow to Infinity. | |
| 111 testJson("1e+400", double.INFINITY); | |
| 112 // (Integers do not, but we don't have those on dart2js). | |
| 113 | |
| 114 // Integer part cannot be omitted: | |
| 115 testError(integers: ""); | |
| 116 | |
| 117 // Test for "Initial zero only allowed for zero integer part" moved to | |
| 118 // json_strict_test.dart because IE's JSON.decode accepts additional initial | |
| 119 // zeros. | |
| 120 | |
| 121 // Only minus allowed as sign. | |
| 122 testError(signs: "+"); | |
| 123 // Requires digits after decimal point. | |
| 124 testError(fractions: "."); | |
| 125 // Requires exponent digts, and only digits. | |
| 126 testError(exponents: ["e", "e+", "e-", "e.0"]); | |
| 127 | |
| 128 // No whitespace inside numbers. | |
| 129 // Additional case "- 2.2e+2" in json_strict_test.dart. | |
| 130 testThrows("-2 .2e+2"); | |
| 131 testThrows("-2. 2e+2"); | |
| 132 testThrows("-2.2 e+2"); | |
| 133 testThrows("-2.2e +2"); | |
| 134 testThrows("-2.2e+ 2"); | |
| 135 | |
| 136 testThrows("[2.,2]"); | |
| 137 testThrows("{2.:2}"); | |
| 138 } | |
| 139 | |
| 140 testStrings() { | |
| 141 // String parser accepts and understands escapes. | |
| 142 var input = r'"\u0000\uffff\n\r\f\t\b\/\\\"' '\x20\ufffd\uffff"'; | |
| 143 var expected = "\u0000\uffff\n\r\f\t\b\/\\\"\x20\ufffd\uffff"; | |
| 144 testJson(input, expected); | |
| 145 // Empty string. | |
| 146 testJson(r'""', ""); | |
| 147 // Escape first. | |
| 148 testJson(r'"\"........"', "\"........"); | |
| 149 // Escape last. | |
| 150 testJson(r'"........\""', "........\""); | |
| 151 // Escape middle. | |
| 152 testJson(r'"....\"...."', "....\"...."); | |
| 153 | |
| 154 // Does not accept single quotes. | |
| 155 testThrows(r"''"); | |
| 156 // Throws on unterminated strings. | |
| 157 testThrows(r'"......\"'); | |
| 158 // Throws on unterminated escapes. | |
| 159 testThrows(r'"\'); // ' is not escaped. | |
| 160 testThrows(r'"\a"'); | |
| 161 testThrows(r'"\u"'); | |
| 162 testThrows(r'"\u1"'); | |
| 163 testThrows(r'"\u12"'); | |
| 164 testThrows(r'"\u123"'); | |
| 165 testThrows(r'"\ux"'); | |
| 166 testThrows(r'"\u1x"'); | |
| 167 testThrows(r'"\u12x"'); | |
| 168 testThrows(r'"\u123x"'); | |
| 169 // Throws on bad escapes. | |
| 170 testThrows(r'"\a"'); | |
| 171 testThrows(r'"\x00"'); | |
| 172 testThrows(r'"\c2"'); | |
| 173 testThrows(r'"\000"'); | |
| 174 testThrows(r'"\u{0}"'); | |
| 175 testThrows(r'"\%"'); | |
| 176 testThrows('"\\\x00"'); // Not raw string! | |
| 177 // Throws on control characters. | |
| 178 for (int i = 0; i < 32; i++) { | |
| 179 var string = new String.fromCharCodes([0x22,i,0x22]); // '"\x00"' etc. | |
| 180 testThrows(string); | |
| 181 } | |
| 182 } | |
| 183 | |
| 184 | |
| 185 testObjects() { | |
| 186 testJson(r'{}', {}); | |
| 187 testJson(r'{"x":42}', {"x":42}); | |
| 188 testJson(r'{"x":{"x":{"x":42}}}', {"x": {"x": {"x": 42}}}); | |
| 189 testJson(r'{"x":10,"x":42}', {"x": 42}); | |
| 190 testJson(r'{"":42}', {"": 42}); | |
| 191 | |
| 192 // Keys must be strings. | |
| 193 testThrows(r'{x:10}'); | |
| 194 testThrows(r'{true:10}'); | |
| 195 testThrows(r'{false:10}'); | |
| 196 testThrows(r'{null:10}'); | |
| 197 testThrows(r'{42:10}'); | |
| 198 testThrows(r'{42e1:10}'); | |
| 199 testThrows(r'{-42:10}'); | |
| 200 testThrows(r'{["text"]:10}'); | |
| 201 testThrows(r'{:10}'); | |
| 202 } | |
| 203 | |
| 204 testArrays() { | |
| 205 testJson(r'[]', []); | |
| 206 testJson(r'[1.1e1,"string",true,false,null,{}]', | |
| 207 [1.1e1, "string", true, false, null, {}]); | |
| 208 testJson(r'[[[[[[]]]],[[[]]],[[]]]]', [[[[[[]]]],[[[]]],[[]]]]); | |
| 209 testJson(r'[{},[{}],{"x":[]}]', [{},[{}],{"x":[]}]); | |
| 210 | |
| 211 testThrows(r'[1,,2]'); | |
| 212 testThrows(r'[1,2,]'); | |
| 213 testThrows(r'[,2]'); | |
| 214 } | |
| 215 | |
| 216 testWords() { | |
| 217 testJson(r'true', true); | |
| 218 testJson(r'false', false); | |
| 219 testJson(r'null', null); | |
| 220 testJson(r'[true]', [true]); | |
| 221 testJson(r'{"true":true}', {"true": true}); | |
| 222 | |
| 223 testThrows(r'truefalse'); | |
| 224 testThrows(r'trues'); | |
| 225 testThrows(r'nulll'); | |
| 226 testThrows(r'full'); | |
| 227 testThrows(r'nul'); | |
| 228 testThrows(r'tru'); | |
| 229 testThrows(r'fals'); | |
| 230 testThrows(r'\null'); | |
| 231 testThrows(r't\rue'); | |
| 232 testThrows(r't\rue'); | |
| 233 } | |
| 234 | |
| 235 testWhitespace() { | |
| 236 // Valid white-space characters. | |
| 237 var v = '\t\r\n\ '; | |
| 238 // Invalid white-space and non-recognized characters. | |
| 239 var invalids = ['\x00', '\f', '\x08', '\\', '\xa0','\u2028', '\u2029']; | |
| 240 | |
| 241 // Valid whitespace accepted "everywhere". | |
| 242 testJson('$v[${v}-2.2e2$v,$v{$v"key"$v:${v}true$v}$v,$v"ab"$v]$v', | |
| 243 [-2.2e2, {"key": true}, "ab"]); | |
| 244 | |
| 245 // IE9 accepts invalid characters at the end, so some of these tests have been | |
| 246 // moved to json_strict_test.dart. | |
| 247 for (var i in invalids) { | |
| 248 testThrows('${i}"s"'); | |
| 249 testThrows('42${i}'); | |
| 250 testThrows('$i[]'); | |
| 251 testThrows('[$i]'); | |
| 252 testThrows('[$i"s"]'); | |
| 253 testThrows('["s"$i]'); | |
| 254 testThrows('$i{"k":"v"}'); | |
| 255 testThrows('{$i"k":"v"}'); | |
| 256 testThrows('{"k"$i:"v"}'); | |
| 257 testThrows('{"k":$i"v"}'); | |
| 258 testThrows('{"k":"v"$i}'); | |
| 259 } | |
| 260 } | |
| 261 | |
| 262 main() { | |
| 263 testNumbers(); | |
| 264 testStrings(); | |
| 265 testWords(); | |
| 266 testObjects(); | |
| 267 testArrays(); | |
| 268 testWhitespace(); | |
| 269 } | |
| OLD | NEW |