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