| // 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)); |
| } |