|  | // Copyright (c) 2011, 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_map_test; | 
|  |  | 
|  | import "package:expect/expect.dart"; | 
|  | import 'dart:convert' show json; | 
|  | import 'dart:collection' show LinkedHashMap, HashMap; | 
|  |  | 
|  | bool useReviver = false; | 
|  | Map<String, dynamic> jsonify(Map map) { | 
|  | String encoded = json.encode(map); | 
|  | return useReviver | 
|  | ? json.decode(encoded, reviver: (key, value) => value) | 
|  | : json.decode(encoded); | 
|  | } | 
|  |  | 
|  | List listEach(Map map) { | 
|  | var result = []; | 
|  | map.forEach((key, value) { | 
|  | result.add(key); | 
|  | result.add(value); | 
|  | }); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | void main() { | 
|  | test(false); | 
|  | test(true); | 
|  | } | 
|  |  | 
|  | void test(bool revive) { | 
|  | useReviver = revive; | 
|  | testEmpty(jsonify({})); | 
|  | testAtoB(jsonify({'a': 'b'})); | 
|  |  | 
|  | // You can write 'Map<String, dynamic>' here (or 'var' which infers the | 
|  | // same), but if you write just 'Map' as the type, then the type of the | 
|  | // constant argument in the addAll below is not inferred correctly. | 
|  | var map = jsonify({}); | 
|  | map['a'] = 'b'; | 
|  | testAtoB(map); | 
|  |  | 
|  | map = jsonify({}); | 
|  | Expect.equals('b', map.putIfAbsent('a', () => 'b')); | 
|  | testAtoB(map); | 
|  |  | 
|  | map = jsonify({}); | 
|  | map.addAll({'a': 'b'}); | 
|  | testAtoB(map); | 
|  |  | 
|  | testOrder(['a', 'b', 'c', 'd', 'e', 'f']); | 
|  |  | 
|  | testProto(); | 
|  | testToString(); | 
|  | testConcurrentModifications(); | 
|  | testType(); | 
|  | testClear(); | 
|  |  | 
|  | testListEntry(); | 
|  | testMutation(); | 
|  | } | 
|  |  | 
|  | void testEmpty(Map map) { | 
|  | for (int i = 0; i < 2; i++) { | 
|  | Expect.equals(0, map.length); | 
|  | Expect.isTrue(map.isEmpty); | 
|  | Expect.isFalse(map.isNotEmpty); | 
|  | Expect.listEquals([], map.keys.toList()); | 
|  | Expect.listEquals([], map.values.toList()); | 
|  | Expect.isNull(map['a']); | 
|  | Expect.listEquals([], listEach(map)); | 
|  | Expect.isFalse(map.containsKey('a')); | 
|  | Expect.isFalse(map.containsValue('a')); | 
|  | Expect.isNull(map.remove('a')); | 
|  | testLookupNonExistingKeys(map); | 
|  | testLookupNonExistingValues(map); | 
|  | map.clear(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void testAtoB(Map map) { | 
|  | Expect.equals(1, map.length); | 
|  | Expect.isFalse(map.isEmpty); | 
|  | Expect.isTrue(map.isNotEmpty); | 
|  | Expect.listEquals(['a'], map.keys.toList()); | 
|  | Expect.listEquals(['b'], map.values.toList()); | 
|  | Expect.equals('b', map['a']); | 
|  | Expect.listEquals(['a', 'b'], listEach(map)); | 
|  | Expect.isTrue(map.containsKey('a')); | 
|  | Expect.isFalse(map.containsKey('b')); | 
|  | Expect.isTrue(map.containsValue('b')); | 
|  | Expect.isFalse(map.containsValue('a')); | 
|  |  | 
|  | testLookupNonExistingKeys(map); | 
|  | testLookupNonExistingValues(map); | 
|  | Expect.equals('b', map.remove('a')); | 
|  | Expect.isNull(map.remove('b')); | 
|  | testLookupNonExistingKeys(map); | 
|  | testLookupNonExistingValues(map); | 
|  |  | 
|  | map.clear(); | 
|  | testEmpty(map); | 
|  | } | 
|  |  | 
|  | void testLookupNonExistingKeys(Map map) { | 
|  | for (var key in ['__proto__', 'null', null]) { | 
|  | Expect.isNull(map[key]); | 
|  | Expect.isFalse(map.containsKey(key)); | 
|  | } | 
|  | } | 
|  |  | 
|  | void testLookupNonExistingValues(Map map) { | 
|  | for (var value in ['__proto__', 'null', null]) { | 
|  | Expect.isFalse(map.containsValue(value)); | 
|  | } | 
|  | } | 
|  |  | 
|  | void testOrder(List list) { | 
|  | if (list.isEmpty) | 
|  | return; | 
|  | else | 
|  | testOrder(list.skip(1).toList()); | 
|  |  | 
|  | Map original = {}; | 
|  | for (int i = 0; i < list.length; i++) { | 
|  | original[list[i]] = i; | 
|  | } | 
|  |  | 
|  | Map map = jsonify(original); | 
|  | Expect.equals(list.length, map.length); | 
|  | Expect.listEquals(list, map.keys.toList()); | 
|  |  | 
|  | for (int i = 0; i < 10; i++) { | 
|  | map["$i"] = i; | 
|  | Expect.equals(list.length + i + 1, map.length); | 
|  | Expect.listEquals(list, map.keys.take(list.length).toList()); | 
|  | } | 
|  | } | 
|  |  | 
|  | void testProto() { | 
|  | Map map = jsonify({'__proto__': 0}); | 
|  | Expect.equals(1, map.length); | 
|  | Expect.isTrue(map.containsKey('__proto__')); | 
|  | Expect.listEquals(['__proto__'], map.keys.toList()); | 
|  | Expect.equals(0, map['__proto__']); | 
|  | Expect.equals(0, map.remove('__proto__')); | 
|  | testEmpty(map); | 
|  |  | 
|  | map = jsonify({'__proto__': null}); | 
|  | Expect.equals(1, map.length); | 
|  | Expect.isTrue(map.containsKey('__proto__')); | 
|  | Expect.listEquals(['__proto__'], map.keys.toList()); | 
|  | Expect.isNull(map['__proto__']); | 
|  | Expect.isNull(map.remove('__proto__')); | 
|  | testEmpty(map); | 
|  | } | 
|  |  | 
|  | void testToString() { | 
|  | Expect.equals("{}", jsonify({}).toString()); | 
|  | Expect.equals("{a: 0}", jsonify({'a': 0}).toString()); | 
|  | } | 
|  |  | 
|  | void testConcurrentModifications() { | 
|  | void testIterate(Map map, Iterable iterable, Function f) { | 
|  | Iterator iterator = iterable.iterator; | 
|  | f(map); | 
|  | iterator.moveNext(); | 
|  | } | 
|  |  | 
|  | void testKeys(Map map, Function f) => testIterate(map, map.keys, f); | 
|  | void testValues(Map map, Function f) => testIterate(map, map.values, f); | 
|  |  | 
|  | void testForEach(Map map, Function f) { | 
|  | map.forEach((key, value) { | 
|  | f(map); | 
|  | }); | 
|  | } | 
|  |  | 
|  | bool throwsCME(Function f) { | 
|  | try { | 
|  | f(); | 
|  | } on ConcurrentModificationError catch (e) { | 
|  | return true; | 
|  | } catch (e) { | 
|  | return false; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | Map map = {}; | 
|  | Expect.isTrue(throwsCME(() => testKeys(jsonify(map), (map) => map['a'] = 0))); | 
|  | Expect | 
|  | .isTrue(throwsCME(() => testValues(jsonify(map), (map) => map['a'] = 0))); | 
|  | Expect.isFalse( | 
|  | throwsCME(() => testForEach(jsonify(map), (map) => map['a'] = 0))); | 
|  |  | 
|  | Expect.isFalse(throwsCME(() => testKeys(jsonify(map), (map) => map.clear()))); | 
|  | Expect | 
|  | .isFalse(throwsCME(() => testValues(jsonify(map), (map) => map.clear()))); | 
|  | Expect.isFalse( | 
|  | throwsCME(() => testForEach(jsonify(map), (map) => map.clear()))); | 
|  |  | 
|  | Expect.isFalse( | 
|  | throwsCME(() => testKeys(jsonify(map), (map) => map.remove('a')))); | 
|  | Expect.isFalse( | 
|  | throwsCME(() => testValues(jsonify(map), (map) => map.remove('a')))); | 
|  | Expect.isFalse( | 
|  | throwsCME(() => testForEach(jsonify(map), (map) => map.remove('a')))); | 
|  |  | 
|  | Expect.isTrue(throwsCME( | 
|  | () => testKeys(jsonify(map), (map) => map.putIfAbsent('a', () => 0)))); | 
|  | Expect.isTrue(throwsCME( | 
|  | () => testValues(jsonify(map), (map) => map.putIfAbsent('a', () => 0)))); | 
|  | Expect.isFalse(throwsCME( | 
|  | () => testForEach(jsonify(map), (map) => map.putIfAbsent('a', () => 0)))); | 
|  |  | 
|  | Expect.isFalse( | 
|  | throwsCME(() => testKeys(jsonify(map), (map) => map.addAll({})))); | 
|  | Expect.isFalse( | 
|  | throwsCME(() => testValues(jsonify(map), (map) => map.addAll({})))); | 
|  | Expect.isFalse( | 
|  | throwsCME(() => testForEach(jsonify(map), (map) => map.addAll({})))); | 
|  |  | 
|  | Expect.isTrue( | 
|  | throwsCME(() => testKeys(jsonify(map), (map) => map.addAll({'a': 0})))); | 
|  | Expect.isTrue( | 
|  | throwsCME(() => testValues(jsonify(map), (map) => map.addAll({'a': 0})))); | 
|  | Expect.isFalse(throwsCME( | 
|  | () => testForEach(jsonify(map), (map) => map.addAll({'a': 0})))); | 
|  |  | 
|  | map = {'a': 1}; | 
|  | Expect | 
|  | .isFalse(throwsCME(() => testKeys(jsonify(map), (map) => map['a'] = 0))); | 
|  | Expect.isFalse( | 
|  | throwsCME(() => testValues(jsonify(map), (map) => map['a'] = 0))); | 
|  | Expect.isFalse( | 
|  | throwsCME(() => testForEach(jsonify(map), (map) => map['a'] = 0))); | 
|  |  | 
|  | Expect.isTrue(throwsCME(() => testKeys(jsonify(map), (map) => map['b'] = 0))); | 
|  | Expect | 
|  | .isTrue(throwsCME(() => testValues(jsonify(map), (map) => map['b'] = 0))); | 
|  | Expect.isTrue( | 
|  | throwsCME(() => testForEach(jsonify(map), (map) => map['b'] = 0))); | 
|  |  | 
|  | Expect.isTrue(throwsCME(() => testKeys(jsonify(map), (map) => map.clear()))); | 
|  | Expect | 
|  | .isTrue(throwsCME(() => testValues(jsonify(map), (map) => map.clear()))); | 
|  | Expect | 
|  | .isTrue(throwsCME(() => testForEach(jsonify(map), (map) => map.clear()))); | 
|  |  | 
|  | Expect.isTrue( | 
|  | throwsCME(() => testKeys(jsonify(map), (map) => map.remove('a')))); | 
|  | Expect.isTrue( | 
|  | throwsCME(() => testValues(jsonify(map), (map) => map.remove('a')))); | 
|  | Expect.isTrue( | 
|  | throwsCME(() => testForEach(jsonify(map), (map) => map.remove('a')))); | 
|  |  | 
|  | Expect.isFalse( | 
|  | throwsCME(() => testKeys(jsonify(map), (map) => map.remove('b')))); | 
|  | Expect.isFalse( | 
|  | throwsCME(() => testValues(jsonify(map), (map) => map.remove('b')))); | 
|  | Expect.isFalse( | 
|  | throwsCME(() => testForEach(jsonify(map), (map) => map.remove('b')))); | 
|  |  | 
|  | Expect.isFalse(throwsCME( | 
|  | () => testKeys(jsonify(map), (map) => map.putIfAbsent('a', () => 0)))); | 
|  | Expect.isFalse(throwsCME( | 
|  | () => testValues(jsonify(map), (map) => map.putIfAbsent('a', () => 0)))); | 
|  | Expect.isFalse(throwsCME( | 
|  | () => testForEach(jsonify(map), (map) => map.putIfAbsent('a', () => 0)))); | 
|  |  | 
|  | Expect.isTrue(throwsCME( | 
|  | () => testKeys(jsonify(map), (map) => map.putIfAbsent('b', () => 0)))); | 
|  | Expect.isTrue(throwsCME( | 
|  | () => testValues(jsonify(map), (map) => map.putIfAbsent('b', () => 0)))); | 
|  | Expect.isTrue(throwsCME( | 
|  | () => testForEach(jsonify(map), (map) => map.putIfAbsent('b', () => 0)))); | 
|  |  | 
|  | Expect.isFalse( | 
|  | throwsCME(() => testKeys(jsonify(map), (map) => map.addAll({})))); | 
|  | Expect.isFalse( | 
|  | throwsCME(() => testValues(jsonify(map), (map) => map.addAll({})))); | 
|  | Expect.isFalse( | 
|  | throwsCME(() => testForEach(jsonify(map), (map) => map.addAll({})))); | 
|  |  | 
|  | Expect.isFalse( | 
|  | throwsCME(() => testKeys(jsonify(map), (map) => map.addAll({'a': 0})))); | 
|  | Expect.isFalse( | 
|  | throwsCME(() => testValues(jsonify(map), (map) => map.addAll({'a': 0})))); | 
|  | Expect.isFalse(throwsCME( | 
|  | () => testForEach(jsonify(map), (map) => map.addAll({'a': 0})))); | 
|  |  | 
|  | Expect.isTrue( | 
|  | throwsCME(() => testKeys(jsonify(map), (map) => map.addAll({'b': 0})))); | 
|  | Expect.isTrue( | 
|  | throwsCME(() => testValues(jsonify(map), (map) => map.addAll({'b': 0})))); | 
|  | Expect.isTrue(throwsCME( | 
|  | () => testForEach(jsonify(map), (map) => map.addAll({'b': 0})))); | 
|  | } | 
|  |  | 
|  | void testType() { | 
|  | var map = jsonify({}); | 
|  | var type = "${map.runtimeType}"; | 
|  |  | 
|  | // The documentation of json.decode doesn't actually specify that it returns | 
|  | // a map (it's marked dynamic), but it's a reasonable expectation if you | 
|  | // don't provide a reviver function. | 
|  | Expect.isTrue(map is Map, type); | 
|  | Expect.isTrue(map is Map<String, dynamic>, type); | 
|  | Expect.isFalse(map is Map<int, dynamic>, type); | 
|  | } | 
|  |  | 
|  | void testClear() { | 
|  | Map map = jsonify({'a': 0}); | 
|  | map.clear(); | 
|  | Expect.equals(0, map.length); | 
|  | } | 
|  |  | 
|  | void testListEntry() { | 
|  | Map map = jsonify({ | 
|  | 'a': [ | 
|  | 7, | 
|  | 8, | 
|  | {'b': 9} | 
|  | ] | 
|  | }); | 
|  | List list = map['a']; | 
|  | Expect.equals(3, list.length); | 
|  | Expect.equals(7, list[0]); | 
|  | Expect.equals(8, list[1]); | 
|  | Expect.equals(9, list[2]['b']); | 
|  | } | 
|  |  | 
|  | void testMutation() { | 
|  | Map map = jsonify({'a': 0}); | 
|  | Expect.listEquals(['a', 0], listEach(map)); | 
|  | map['a'] = 1; | 
|  | Expect.listEquals(['a', 1], listEach(map)); | 
|  | map['a']++; | 
|  | Expect.listEquals(['a', 2], listEach(map)); | 
|  | } |