Index: tests/corelib/json_test.dart |
diff --git a/tests/corelib/json_test.dart b/tests/corelib/json_test.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..e0ece7bf869e613e7cb66a8a3c0e243215579914 |
--- /dev/null |
+++ b/tests/corelib/json_test.dart |
@@ -0,0 +1,249 @@ |
+// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+import "dart:json"; |
+ |
+bool badFormat(e) => e is FormatException; |
+ |
+void testJson(json, expected) { |
+ var value = parse(json); |
+ compare(expected, actual, path) { |
+ if (expected is List) { |
+ Expect.isTrue(actual is List); |
+ Expect.equals(expected.length, actual.length, "$path: List length"); |
+ for (int i = 0; i < expected.length; i++) { |
+ compare(expected[i], actual[i], "$path[$i]"); |
+ } |
+ } else if (expected is Map) { |
+ Expect.isTrue(actual is Map); |
+ Expect.equals(expected.length, actual.length, "$path: Map size"); |
+ expected.forEach((key, value) { |
+ Expect.isTrue(actual.containsKey(key)); |
+ compare(value, actual[key], "$path[$key]"); |
+ }); |
+ } else if (expected is num) { |
+ Expect.equals(expected is int, actual is int, "$path: same number type"); |
+ Expect.isTrue(expected.compareTo(actual) == 0, |
+ "$path: $expected vs. $actual"); |
+ } else { |
+ // String, bool, null. |
+ Expect.equals(expected, actual, path); |
+ } |
+ } |
+ compare(expected, value, "value"); |
+} |
+ |
+void testThrows(json) { |
+ Expect.throws(() => parse(json), badFormat); |
+} |
+ |
+testNumbers() { |
+ // Positive tests for number formats. |
+ var integerList = ["0","9","9999"]; |
+ var signList = ["", "-"]; |
+ var fractionList = ["", ".0", ".1", ".99999"]; |
+ var exponentList = [""]; |
+ for (var exphead in ["e", "E", "e-", "E-", "e+", "E+"]) { |
+ for (var expval in ["0", "1", "200"]) { |
+ exponentList.add("$exphead$expval"); |
+ } |
+ } |
+ |
+ for (var integer in integerList) { |
+ for (var sign in signList) { |
+ for (var fraction in fractionList) { |
+ for (var exp in exponentList) { |
+ var literal = "$sign$integer$fraction$exp"; |
+ var parseNumber = |
+ ((fraction == "" && exp == "") ? (String x) => int.parse(x) |
+ : (String x) => double.parse(x)); |
+ var expectedValue = parseNumber(literal); |
+ testJson(literal, expectedValue); |
+ } |
+ } |
+ } |
+ } |
+ |
+ // Negative tests (syntax error). |
+ // testError thoroughly tests the given parts with a lot of valid |
+ // values for the other parts. |
+ testError({signs, integers, fractions, exponents}) { |
+ def(value, defaultValue) { |
+ if (value == null) return defaultValue; |
+ if (value is List) return value; |
+ return [value]; |
+ } |
+ signs = def(signs, signList); |
+ integers = def(integers, integerList); |
+ fractions = def(fractions, fractionList); |
+ exponents = def(exponents, exponentList); |
+ for (var integer in integers) { |
+ for (var sign in signs) { |
+ for (var fraction in fractions) { |
+ for (var exponent in exponents) { |
+ var literal = "$sign$integer$fraction$exponent"; |
+ testThrows(literal); |
+ } |
+ } |
+ } |
+ } |
+ } |
+ // Doubles overflow to Infinity. |
+ testJson("1e+400", double.INFINITY); |
+ // (Integers do not, but we don't have those on dart2js). |
+ |
+ // Integer part cannot be omitted: |
+ testError(integers: ""); |
+ // Initial zero only allowed for zero integer part. |
+ testError(integers: ["00", "01"]); |
+ // Only minus allowed as sign. |
+ testError(signs: "+"); |
+ // Requires digits after decimal point. |
+ testError(fractions: "."); |
+ // Requires exponent digts, and only digits. |
+ testError(exponents: ["e", "e+", "e-", "e.0"]); |
+ |
+ // No whitespace inside numbers. |
+ testThrows("- 2.2e+2"); |
+ testThrows("-2 .2e+2"); |
+ testThrows("-2. 2e+2"); |
+ testThrows("-2.2 e+2"); |
+ testThrows("-2.2e +2"); |
+ testThrows("-2.2e+ 2"); |
+ |
+ testThrows("[2.,2]"); |
+ testThrows("{2.:2}"); |
+} |
+ |
+testStrings() { |
+ // String parser accepts and understands escapes. |
+ var input = r'"\u0000\uffff\n\r\f\t\b\/\\\"' '\x20\ufffd\uffff"'; |
+ var expected = "\u0000\uffff\n\r\f\t\b\/\\\"\x20\ufffd\uffff"; |
+ testJson(input, expected); |
+ // Empty string. |
+ testJson(r'""', ""); |
+ // Escape first. |
+ testJson(r'"\"........"', "\"........"); |
+ // Escape last. |
+ testJson(r'"........\""', "........\""); |
+ // Escape middle. |
+ testJson(r'"....\"...."', "....\"...."); |
+ |
+ // Does not accept single quotes. |
+ testThrows(r"''"); |
+ // Throws on unterminated strings. |
+ testThrows(r'"......\"'); |
+ // Throws on unterminated escapes. |
+ testThrows(r'"\'); // ' is not escaped. |
+ testThrows(r'"\a"'); |
+ testThrows(r'"\u"'); |
+ testThrows(r'"\u1"'); |
+ testThrows(r'"\u12"'); |
+ testThrows(r'"\u123"'); |
+ testThrows(r'"\ux"'); |
+ testThrows(r'"\u1x"'); |
+ testThrows(r'"\u12x"'); |
+ testThrows(r'"\u123x"'); |
+ // Throws on bad escapes. |
+ testThrows(r'"\a"'); |
+ testThrows(r'"\x00"'); |
+ testThrows(r'"\c2"'); |
+ testThrows(r'"\000"'); |
+ testThrows(r'"\u{0}"'); |
+ testThrows(r'"\%"'); |
+ testThrows('"\\\x00"'); // Not raw string! |
+ // Throws on control characters. |
+ for (int i = 0; i < 32; i++) { |
+ var string = new String.fromCharCodes([0x22,i,0x22]); // '"\x00"' etc. |
+ testThrows(string); |
+ } |
+} |
+ |
+ |
+testObjects() { |
+ testJson(r'{}', {}); |
+ testJson(r'{"x":42}', {"x":42}); |
+ testJson(r'{"x":{"x":{"x":42}}}', {"x": {"x": {"x": 42}}}); |
+ testJson(r'{"x":10,"x":42}', {"x": 42}); |
+ testJson(r'{"":42}', {"": 42}); |
+ |
+ // Keys must be strings. |
+ testThrows(r'{x:10}'); |
+ testThrows(r'{true:10}'); |
+ testThrows(r'{false:10}'); |
+ testThrows(r'{null:10}'); |
+ testThrows(r'{42:10}'); |
+ testThrows(r'{42e1:10}'); |
+ testThrows(r'{-42:10}'); |
+ testThrows(r'{["text"]:10}'); |
+ testThrows(r'{:10}'); |
+} |
+ |
+testArrays() { |
+ testJson(r'[]', []); |
+ testJson(r'[1.1e1,"string",true,false,null,{}]', |
+ [1.1e1, "string", true, false, null, {}]); |
+ testJson(r'[[[[[[]]]],[[[]]],[[]]]]', [[[[[[]]]],[[[]]],[[]]]]); |
+ testJson(r'[{},[{}],{"x":[]}]', [{},[{}],{"x":[]}]); |
+ |
+ testThrows(r'[1,,2]'); |
+ testThrows(r'[1,2,]'); |
+ testThrows(r'[,2]'); |
+} |
+ |
+testWords() { |
+ testJson(r'true', true); |
+ testJson(r'false', false); |
+ testJson(r'null', null); |
+ testJson(r'[true]', [true]); |
+ testJson(r'{"true":true}', {"true": true}); |
+ |
+ testThrows(r'truefalse'); |
+ testThrows(r'trues'); |
+ testThrows(r'nulll'); |
+ testThrows(r'full'); |
+ testThrows(r'nul'); |
+ testThrows(r'tru'); |
+ testThrows(r'fals'); |
+ testThrows(r'\null'); |
+ testThrows(r't\rue'); |
+ testThrows(r't\rue'); |
+} |
+ |
+testWhitespace() { |
+ // Valid white-space characters. |
+ var v = '\t\r\n\ '; |
+ // Invalid white-space and non-recognized characters. |
+ var invalids = ['\x00', '\f', '\x08', '\\', '\xa0','\u2028', '\u2029']; |
+ |
+ // Valid whitespace accepted "everywhere". |
+ testJson('$v[${v}-2.2e2$v,$v{$v"key"$v:${v}true$v}$v,$v"ab"$v]$v', |
+ [-2.2e2, {"key": true}, "ab"]); |
+ |
+ for (var i in invalids) { |
+ testThrows('${i}"s"'); |
+ testThrows('"s"${i}'); |
+ testThrows('42${i}'); |
+ testThrows('$i[]'); |
+ testThrows('[$i]'); |
+ testThrows('[$i"s"]'); |
+ testThrows('["s"$i]'); |
+ testThrows('[]$i'); |
+ testThrows('$i{"k":"v"}'); |
+ testThrows('{$i"k":"v"}'); |
+ testThrows('{"k"$i:"v"}'); |
+ testThrows('{"k":$i"v"}'); |
+ testThrows('{"k":"v"$i}'); |
+ testThrows('{"k":"v"}$i'); |
+ } |
+} |
+ |
+main() { |
+ testNumbers(); |
+ testStrings(); |
+ testWords(); |
+ testObjects(); |
+ testArrays(); |
+ testWhitespace(); |
+} |