52f5c2ff22
Review URL: https://codereview.chromium.org//11778030 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@16769 260f80e4-7a28-3924-810f-c04153c831b5
252 lines
7.1 KiB
Dart
252 lines
7.1 KiB
Dart
// 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.
|
|
|
|
library json_test;
|
|
|
|
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();
|
|
}
|