OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011, 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 library map_test; | |
6 | |
7 import "package:expect/expect.dart"; | |
8 import 'dart:collection'; | |
9 import 'dart:convert' show JSON; | |
10 | |
11 Map newJsonMap() => JSON.decode('{}'); | |
12 Map newJsonMapCustomReviver() => | |
13 JSON.decode('{}', reviver: (key, value) => value); | |
14 | |
15 void main() { | |
16 test(new HashMap()); | |
17 test(new LinkedHashMap()); | |
18 test(new SplayTreeMap()); | |
19 test(new SplayTreeMap(Comparable.compare)); | |
20 test(new MapView(new HashMap())); | |
21 test(new MapView(new SplayTreeMap())); | |
22 test(new MapBaseMap()); | |
23 test(new MapMixinMap()); | |
24 test(newJsonMap()); | |
25 test(newJsonMapCustomReviver()); | |
26 testLinkedHashMap(); | |
27 testMapLiteral(); | |
28 testNullValue(); | |
29 testTypes(); | |
30 | |
31 testWeirdStringKeys(new Map()); | |
32 testWeirdStringKeys(new Map<String, String>()); | |
33 testWeirdStringKeys(new HashMap()); | |
34 testWeirdStringKeys(new HashMap<String, String>()); | |
35 testWeirdStringKeys(new LinkedHashMap()); | |
36 testWeirdStringKeys(new LinkedHashMap<String, String>()); | |
37 testWeirdStringKeys(new SplayTreeMap()); | |
38 testWeirdStringKeys(new SplayTreeMap<String, String>()); | |
39 testWeirdStringKeys(new MapBaseMap<String, String>()); | |
40 testWeirdStringKeys(new MapMixinMap<String, String>()); | |
41 testWeirdStringKeys(newJsonMap()); | |
42 testWeirdStringKeys(newJsonMapCustomReviver()); | |
43 | |
44 testNumericKeys(new Map()); | |
45 testNumericKeys(new Map<num, String>()); | |
46 testNumericKeys(new HashMap()); | |
47 testNumericKeys(new HashMap<num, String>()); | |
48 testNumericKeys(new HashMap.identity()); | |
49 testNumericKeys(new HashMap<num, String>.identity()); | |
50 testNumericKeys(new LinkedHashMap()); | |
51 testNumericKeys(new LinkedHashMap<num, String>()); | |
52 testNumericKeys(new LinkedHashMap.identity()); | |
53 testNumericKeys(new LinkedHashMap<num, String>.identity()); | |
54 testNumericKeys(new MapBaseMap<num, String>()); | |
55 testNumericKeys(new MapMixinMap<num, String>()); | |
56 testNumericKeys(newJsonMap()); | |
57 testNumericKeys(newJsonMapCustomReviver()); | |
58 | |
59 testNaNKeys(new Map()); | |
60 testNaNKeys(new Map<num, String>()); | |
61 testNaNKeys(new HashMap()); | |
62 testNaNKeys(new HashMap<num, String>()); | |
63 testNaNKeys(new LinkedHashMap()); | |
64 testNaNKeys(new LinkedHashMap<num, String>()); | |
65 testNaNKeys(new MapBaseMap<num, String>()); | |
66 testNaNKeys(new MapMixinMap<num, String>()); | |
67 testNaNKeys(newJsonMap()); | |
68 testNaNKeys(newJsonMapCustomReviver()); | |
69 // Identity maps fail the NaN-keys tests because the test assumes that | |
70 // NaN is not equal to NaN. | |
71 | |
72 testIdentityMap(new Map.identity()); | |
73 testIdentityMap(new HashMap.identity()); | |
74 testIdentityMap(new LinkedHashMap.identity()); | |
75 testIdentityMap(new HashMap(equals: identical, hashCode: identityHashCode)); | |
76 testIdentityMap( | |
77 new LinkedHashMap(equals: identical, hashCode: identityHashCode)); | |
78 testIdentityMap(new HashMap( | |
79 equals: (x, y) => identical(x, y), hashCode: (x) => identityHashCode(x))); | |
80 testIdentityMap(new LinkedHashMap( | |
81 equals: (x, y) => identical(x, y), hashCode: (x) => identityHashCode(x))); | |
82 | |
83 testCustomMap(new HashMap( | |
84 equals: myEquals, | |
85 hashCode: myHashCode, | |
86 isValidKey: (v) => v is Customer)); | |
87 testCustomMap(new LinkedHashMap( | |
88 equals: myEquals, | |
89 hashCode: myHashCode, | |
90 isValidKey: (v) => v is Customer)); | |
91 testCustomMap( | |
92 new HashMap<Customer, dynamic>(equals: myEquals, hashCode: myHashCode)); | |
93 | |
94 testCustomMap(new LinkedHashMap<Customer, dynamic>( | |
95 equals: myEquals, hashCode: myHashCode)); | |
96 | |
97 testIterationOrder(new LinkedHashMap()); | |
98 testIterationOrder(new LinkedHashMap.identity()); | |
99 testIterationOrder(newJsonMap()); | |
100 testIterationOrder(newJsonMapCustomReviver()); | |
101 | |
102 testOtherKeys(new SplayTreeMap<int, int>()); | |
103 testOtherKeys( | |
104 new SplayTreeMap<int, int>((int a, int b) => a - b, (v) => v is int)); | |
105 testOtherKeys(new SplayTreeMap((int a, int b) => a - b, (v) => v is int)); | |
106 testOtherKeys(new HashMap<int, int>()); | |
107 testOtherKeys(new HashMap<int, int>.identity()); | |
108 testOtherKeys(new HashMap<int, int>( | |
109 hashCode: (v) => v.hashCode, isValidKey: (v) => v is int)); | |
110 testOtherKeys(new HashMap( | |
111 equals: (int x, int y) => x == y, | |
112 hashCode: (int v) => v.hashCode, | |
113 isValidKey: (v) => v is int)); | |
114 testOtherKeys(new LinkedHashMap<int, int>()); | |
115 testOtherKeys(new LinkedHashMap<int, int>.identity()); | |
116 testOtherKeys(new LinkedHashMap<int, int>( | |
117 hashCode: (v) => v.hashCode, isValidKey: (v) => v is int)); | |
118 testOtherKeys(new LinkedHashMap( | |
119 equals: (int x, int y) => x == y, | |
120 hashCode: (int v) => v.hashCode, | |
121 isValidKey: (v) => v is int)); | |
122 testOtherKeys(new MapBaseMap<int, int>()); | |
123 testOtherKeys(new MapMixinMap<int, int>()); | |
124 testOtherKeys(newJsonMap()); | |
125 testOtherKeys(newJsonMapCustomReviver()); | |
126 | |
127 testUnmodifiableMap(const {1: 37}); | |
128 testUnmodifiableMap(new UnmodifiableMapView({1: 37})); | |
129 testUnmodifiableMap(new UnmodifiableMapBaseMap([1, 37])); | |
130 | |
131 testFrom(); | |
132 } | |
133 | |
134 void test(Map map) { | |
135 testDeletedElement(map); | |
136 testMap(map, 1, 2, 3, 4, 5, 6, 7, 8); | |
137 map.clear(); | |
138 testMap(map, "value1", "value2", "value3", "value4", "value5", "value6", | |
139 "value7", "value8"); | |
140 } | |
141 | |
142 void testLinkedHashMap() { | |
143 LinkedHashMap map = new LinkedHashMap(); | |
144 Expect.equals(false, map.containsKey(1)); | |
145 map[1] = 1; | |
146 map[1] = 2; | |
147 testLength(1, map); | |
148 } | |
149 | |
150 void testMap(Map map, key1, key2, key3, key4, key5, key6, key7, key8) { | |
151 int value1 = 10; | |
152 int value2 = 20; | |
153 int value3 = 30; | |
154 int value4 = 40; | |
155 int value5 = 50; | |
156 int value6 = 60; | |
157 int value7 = 70; | |
158 int value8 = 80; | |
159 | |
160 testLength(0, map); | |
161 | |
162 map[key1] = value1; | |
163 Expect.equals(value1, map[key1]); | |
164 map[key1] = value2; | |
165 Expect.equals(false, map.containsKey(key2)); | |
166 testLength(1, map); | |
167 | |
168 map[key1] = value1; | |
169 Expect.equals(value1, map[key1]); | |
170 // Add enough entries to make sure the table grows. | |
171 map[key2] = value2; | |
172 Expect.equals(value2, map[key2]); | |
173 testLength(2, map); | |
174 map[key3] = value3; | |
175 Expect.equals(value2, map[key2]); | |
176 Expect.equals(value3, map[key3]); | |
177 map[key4] = value4; | |
178 Expect.equals(value3, map[key3]); | |
179 Expect.equals(value4, map[key4]); | |
180 map[key5] = value5; | |
181 Expect.equals(value4, map[key4]); | |
182 Expect.equals(value5, map[key5]); | |
183 map[key6] = value6; | |
184 Expect.equals(value5, map[key5]); | |
185 Expect.equals(value6, map[key6]); | |
186 map[key7] = value7; | |
187 Expect.equals(value6, map[key6]); | |
188 Expect.equals(value7, map[key7]); | |
189 map[key8] = value8; | |
190 Expect.equals(value1, map[key1]); | |
191 Expect.equals(value2, map[key2]); | |
192 Expect.equals(value3, map[key3]); | |
193 Expect.equals(value4, map[key4]); | |
194 Expect.equals(value5, map[key5]); | |
195 Expect.equals(value6, map[key6]); | |
196 Expect.equals(value7, map[key7]); | |
197 Expect.equals(value8, map[key8]); | |
198 testLength(8, map); | |
199 | |
200 map.remove(key4); | |
201 Expect.equals(false, map.containsKey(key4)); | |
202 testLength(7, map); | |
203 | |
204 // Test clearing the table. | |
205 map.clear(); | |
206 testLength(0, map); | |
207 Expect.equals(false, map.containsKey(key1)); | |
208 Expect.equals(false, map.containsKey(key2)); | |
209 Expect.equals(false, map.containsKey(key3)); | |
210 Expect.equals(false, map.containsKey(key4)); | |
211 Expect.equals(false, map.containsKey(key5)); | |
212 Expect.equals(false, map.containsKey(key6)); | |
213 Expect.equals(false, map.containsKey(key7)); | |
214 Expect.equals(false, map.containsKey(key8)); | |
215 | |
216 // Test adding and removing again. | |
217 map[key1] = value1; | |
218 Expect.equals(value1, map[key1]); | |
219 testLength(1, map); | |
220 map[key2] = value2; | |
221 Expect.equals(value2, map[key2]); | |
222 testLength(2, map); | |
223 map[key3] = value3; | |
224 Expect.equals(value3, map[key3]); | |
225 map.remove(key3); | |
226 testLength(2, map); | |
227 map[key4] = value4; | |
228 Expect.equals(value4, map[key4]); | |
229 map.remove(key4); | |
230 testLength(2, map); | |
231 map[key5] = value5; | |
232 Expect.equals(value5, map[key5]); | |
233 map.remove(key5); | |
234 testLength(2, map); | |
235 map[key6] = value6; | |
236 Expect.equals(value6, map[key6]); | |
237 map.remove(key6); | |
238 testLength(2, map); | |
239 map[key7] = value7; | |
240 Expect.equals(value7, map[key7]); | |
241 map.remove(key7); | |
242 testLength(2, map); | |
243 map[key8] = value8; | |
244 Expect.equals(value8, map[key8]); | |
245 map.remove(key8); | |
246 testLength(2, map); | |
247 | |
248 Expect.equals(true, map.containsKey(key1)); | |
249 Expect.equals(true, map.containsValue(value1)); | |
250 | |
251 // Test Map.forEach. | |
252 Map otherMap = new Map(); | |
253 void testForEachMap(key, value) { | |
254 otherMap[key] = value; | |
255 } | |
256 | |
257 map.forEach(testForEachMap); | |
258 Expect.equals(true, otherMap.containsKey(key1)); | |
259 Expect.equals(true, otherMap.containsKey(key2)); | |
260 Expect.equals(true, otherMap.containsValue(value1)); | |
261 Expect.equals(true, otherMap.containsValue(value2)); | |
262 Expect.equals(2, otherMap.length); | |
263 | |
264 otherMap.clear(); | |
265 Expect.equals(0, otherMap.length); | |
266 | |
267 // Test Collection.keys. | |
268 void testForEachCollection(value) { | |
269 otherMap[value] = value; | |
270 } | |
271 | |
272 Iterable keys = map.keys; | |
273 keys.forEach(testForEachCollection); | |
274 Expect.equals(true, otherMap.containsKey(key1)); | |
275 Expect.equals(true, otherMap.containsKey(key2)); | |
276 Expect.equals(true, otherMap.containsValue(key1)); | |
277 Expect.equals(true, otherMap.containsValue(key2)); | |
278 Expect.equals(true, !otherMap.containsKey(value1)); | |
279 Expect.equals(true, !otherMap.containsKey(value2)); | |
280 Expect.equals(true, !otherMap.containsValue(value1)); | |
281 Expect.equals(true, !otherMap.containsValue(value2)); | |
282 Expect.equals(2, otherMap.length); | |
283 otherMap.clear(); | |
284 Expect.equals(0, otherMap.length); | |
285 | |
286 // Test Collection.values. | |
287 Iterable values = map.values; | |
288 values.forEach(testForEachCollection); | |
289 Expect.equals(true, !otherMap.containsKey(key1)); | |
290 Expect.equals(true, !otherMap.containsKey(key2)); | |
291 Expect.equals(true, !otherMap.containsValue(key1)); | |
292 Expect.equals(true, !otherMap.containsValue(key2)); | |
293 Expect.equals(true, otherMap.containsKey(value1)); | |
294 Expect.equals(true, otherMap.containsKey(value2)); | |
295 Expect.equals(true, otherMap.containsValue(value1)); | |
296 Expect.equals(true, otherMap.containsValue(value2)); | |
297 Expect.equals(2, otherMap.length); | |
298 otherMap.clear(); | |
299 Expect.equals(0, otherMap.length); | |
300 | |
301 // Test Map.putIfAbsent. | |
302 map.clear(); | |
303 Expect.equals(false, map.containsKey(key1)); | |
304 map.putIfAbsent(key1, () => 10); | |
305 Expect.equals(true, map.containsKey(key1)); | |
306 Expect.equals(10, map[key1]); | |
307 Expect.equals(10, map.putIfAbsent(key1, () => 11)); | |
308 | |
309 // Test Map.addAll. | |
310 map.clear(); | |
311 otherMap.clear(); | |
312 otherMap[99] = 1; | |
313 otherMap[50] = 50; | |
314 otherMap[1] = 99; | |
315 map.addAll(otherMap); | |
316 Expect.equals(3, map.length); | |
317 Expect.equals(1, map[99]); | |
318 Expect.equals(50, map[50]); | |
319 Expect.equals(99, map[1]); | |
320 otherMap[50] = 42; | |
321 map.addAll(new HashMap.from(otherMap)); | |
322 Expect.equals(3, map.length); | |
323 Expect.equals(1, map[99]); | |
324 Expect.equals(42, map[50]); | |
325 Expect.equals(99, map[1]); | |
326 otherMap[99] = 7; | |
327 map.addAll(new SplayTreeMap.from(otherMap)); | |
328 Expect.equals(3, map.length); | |
329 Expect.equals(7, map[99]); | |
330 Expect.equals(42, map[50]); | |
331 Expect.equals(99, map[1]); | |
332 otherMap.remove(99); | |
333 map[99] = 0; | |
334 map.addAll(otherMap); | |
335 Expect.equals(3, map.length); | |
336 Expect.equals(0, map[99]); | |
337 Expect.equals(42, map[50]); | |
338 Expect.equals(99, map[1]); | |
339 map.clear(); | |
340 otherMap.clear(); | |
341 map.addAll(otherMap); | |
342 Expect.equals(0, map.length); | |
343 } | |
344 | |
345 void testDeletedElement(Map map) { | |
346 map.clear(); | |
347 for (int i = 0; i < 100; i++) { | |
348 map[1] = 2; | |
349 testLength(1, map); | |
350 map.remove(1); | |
351 testLength(0, map); | |
352 } | |
353 testLength(0, map); | |
354 } | |
355 | |
356 void testMapLiteral() { | |
357 Map m = {"a": 1, "b": 2, "c": 3}; | |
358 Expect.equals(3, m.length); | |
359 int sum = 0; | |
360 m.forEach((a, b) { | |
361 sum += b; | |
362 }); | |
363 Expect.equals(6, sum); | |
364 | |
365 List values = m.keys.toList(); | |
366 Expect.equals(3, values.length); | |
367 String first = values[0]; | |
368 String second = values[1]; | |
369 String third = values[2]; | |
370 String all = "${first}${second}${third}"; | |
371 Expect.equals(3, all.length); | |
372 Expect.equals(true, all.contains("a", 0)); | |
373 Expect.equals(true, all.contains("b", 0)); | |
374 Expect.equals(true, all.contains("c", 0)); | |
375 } | |
376 | |
377 void testNullValue() { | |
378 Map m = {"a": 1, "b": null, "c": 3}; | |
379 | |
380 Expect.equals(null, m["b"]); | |
381 Expect.equals(true, m.containsKey("b")); | |
382 Expect.equals(3, m.length); | |
383 | |
384 m["a"] = null; | |
385 m["c"] = null; | |
386 Expect.equals(null, m["a"]); | |
387 Expect.equals(true, m.containsKey("a")); | |
388 Expect.equals(null, m["c"]); | |
389 Expect.equals(true, m.containsKey("c")); | |
390 Expect.equals(3, m.length); | |
391 | |
392 m.remove("a"); | |
393 Expect.equals(2, m.length); | |
394 Expect.equals(null, m["a"]); | |
395 Expect.equals(false, m.containsKey("a")); | |
396 } | |
397 | |
398 void testTypes() { | |
399 testMap(Map<num, String> map) { | |
400 Expect.isTrue(map is Map<num, String>); | |
401 Expect.isTrue(map is! Map<String, dynamic>); | |
402 Expect.isTrue(map is! Map<dynamic, int>); | |
403 | |
404 // Use with properly typed keys and values. | |
405 map[42] = "text1"; | |
406 map[43] = "text2"; | |
407 map[42] = "text3"; | |
408 Expect.equals("text3", map.remove(42)); | |
409 Expect.equals(null, map[42]); | |
410 map[42] = "text4"; | |
411 | |
412 // Ensure that "containsKey", "containsValue" and "remove" | |
413 // accepts any object. | |
414 for (var object in [true, null, new Object()]) { | |
415 Expect.isFalse(map.containsKey(object)); | |
416 Expect.isFalse(map.containsValue(object)); | |
417 Expect.isNull(map.remove(object)); | |
418 Expect.isNull(map[object]); | |
419 } | |
420 } | |
421 | |
422 testMap(new HashMap<int, String>()); | |
423 testMap(new LinkedHashMap<int, String>()); | |
424 testMap(new SplayTreeMap<int, String>()); | |
425 testMap(new SplayTreeMap<int, String>(Comparable.compare)); | |
426 testMap(new SplayTreeMap<int, String>((int a, int b) => a.compareTo(b))); | |
427 testMap(new HashMap<num, String>()); | |
428 testMap(new LinkedHashMap<num, String>()); | |
429 testMap(new SplayTreeMap<num, String>()); | |
430 testMap(new SplayTreeMap<num, String>(Comparable.compare)); | |
431 testMap(new SplayTreeMap<num, String>((num a, num b) => a.compareTo(b))); | |
432 } | |
433 | |
434 void testWeirdStringKeys(Map map) { | |
435 // Test weird keys. | |
436 var weirdKeys = const [ | |
437 'hasOwnProperty', | |
438 'constructor', | |
439 'toLocaleString', | |
440 'propertyIsEnumerable', | |
441 '__defineGetter__', | |
442 '__defineSetter__', | |
443 '__lookupGetter__', | |
444 '__lookupSetter__', | |
445 'isPrototypeOf', | |
446 'toString', | |
447 'valueOf', | |
448 '__proto__', | |
449 '__count__', | |
450 '__parent__', | |
451 '' | |
452 ]; | |
453 Expect.isTrue(map.isEmpty); | |
454 for (var key in weirdKeys) { | |
455 Expect.isFalse(map.containsKey(key)); | |
456 Expect.equals(null, map[key]); | |
457 var value = 'value:$key'; | |
458 map[key] = value; | |
459 Expect.isTrue(map.containsKey(key)); | |
460 Expect.equals(value, map[key]); | |
461 Expect.equals(value, map.remove(key)); | |
462 Expect.isFalse(map.containsKey(key)); | |
463 Expect.equals(null, map[key]); | |
464 } | |
465 Expect.isTrue(map.isEmpty); | |
466 } | |
467 | |
468 void testNumericKeys(Map map) { | |
469 var numericKeys = const [ | |
470 double.INFINITY, | |
471 double.NEGATIVE_INFINITY, | |
472 0, | |
473 0.0, | |
474 -0.0 | |
475 ]; | |
476 | |
477 Expect.isTrue(map.isEmpty); | |
478 for (var key in numericKeys) { | |
479 Expect.isFalse(map.containsKey(key)); | |
480 Expect.equals(null, map[key]); | |
481 var value = 'value:$key'; | |
482 map[key] = value; | |
483 Expect.isTrue(map.containsKey(key)); | |
484 Expect.equals(value, map[key]); | |
485 Expect.equals(value, map.remove(key)); | |
486 Expect.isFalse(map.containsKey(key)); | |
487 Expect.equals(null, map[key]); | |
488 } | |
489 Expect.isTrue(map.isEmpty); | |
490 } | |
491 | |
492 void testNaNKeys(Map map) { | |
493 Expect.isTrue(map.isEmpty); | |
494 // Test NaN. | |
495 var nan = double.NAN; | |
496 Expect.isFalse(map.containsKey(nan)); | |
497 Expect.equals(null, map[nan]); | |
498 | |
499 map[nan] = 'value:0'; | |
500 Expect.isFalse(map.containsKey(nan)); | |
501 Expect.equals(null, map[nan]); | |
502 testLength(1, map); | |
503 | |
504 map[nan] = 'value:1'; | |
505 Expect.isFalse(map.containsKey(nan)); | |
506 Expect.equals(null, map[nan]); | |
507 testLength(2, map); | |
508 | |
509 Expect.equals(null, map.remove(nan)); | |
510 testLength(2, map); | |
511 | |
512 var count = 0; | |
513 map.forEach((key, value) { | |
514 if (key.isNaN) count++; | |
515 }); | |
516 Expect.equals(2, count); | |
517 | |
518 map.clear(); | |
519 Expect.isTrue(map.isEmpty); | |
520 } | |
521 | |
522 void testLength(int length, Map map) { | |
523 Expect.equals(length, map.length); | |
524 Expect.equals(length, map.keys.length); | |
525 Expect.equals(length, map.values.length); | |
526 // Check being-empty. | |
527 var ifEmpty = (length == 0) ? Expect.isTrue : Expect.isFalse; | |
528 var ifNotEmpty = (length != 0) ? Expect.isTrue : Expect.isFalse; | |
529 ifEmpty(map.isEmpty); | |
530 ifNotEmpty(map.isNotEmpty); | |
531 ifEmpty(map.keys.isEmpty); | |
532 ifNotEmpty(map.keys.isNotEmpty); | |
533 ifEmpty(map.values.isEmpty); | |
534 ifNotEmpty(map.values.isNotEmpty); | |
535 // Test key/value iterators match their isEmpty/isNotEmpty. | |
536 ifNotEmpty(map.keys.iterator.moveNext()); | |
537 ifNotEmpty(map.values.iterator.moveNext()); | |
538 if (length == 0) { | |
539 for (var k in map.keys) Expect.fail("contains key when iterating: $k"); | |
540 for (var v in map.values) Expect.fail("contains values when iterating: $v"); | |
541 } | |
542 } | |
543 | |
544 testIdentityMap(Map map) { | |
545 Expect.isTrue(map.isEmpty); | |
546 | |
547 var nan = double.NAN; | |
548 // TODO(11551): Remove guard when dart2js makes identical(NaN, NaN) true. | |
549 if (identical(nan, nan)) { | |
550 map[nan] = 42; | |
551 testLength(1, map); | |
552 Expect.isTrue(map.containsKey(nan)); | |
553 Expect.equals(42, map[nan]); | |
554 map[nan] = 37; | |
555 testLength(1, map); | |
556 Expect.equals(37, map[nan]); | |
557 Expect.equals(37, map.remove(nan)); | |
558 testLength(0, map); | |
559 } | |
560 | |
561 Vampire v1 = const Vampire(1); | |
562 Vampire v2 = const Vampire(2); | |
563 Expect.isFalse(v1 == v1); | |
564 Expect.isFalse(v2 == v2); | |
565 Expect.isTrue(v2 == v1); // Snob! | |
566 | |
567 map[v1] = 1; | |
568 map[v2] = 2; | |
569 testLength(2, map); | |
570 | |
571 Expect.isTrue(map.containsKey(v1)); | |
572 Expect.isTrue(map.containsKey(v2)); | |
573 | |
574 Expect.equals(1, map[v1]); | |
575 Expect.equals(2, map[v2]); | |
576 | |
577 Expect.equals(1, map.remove(v1)); | |
578 testLength(1, map); | |
579 Expect.isFalse(map.containsKey(v1)); | |
580 Expect.isTrue(map.containsKey(v2)); | |
581 | |
582 Expect.isNull(map.remove(v1)); | |
583 Expect.equals(2, map.remove(v2)); | |
584 testLength(0, map); | |
585 | |
586 var eq01 = new Equalizer(0); | |
587 var eq02 = new Equalizer(0); | |
588 var eq11 = new Equalizer(1); | |
589 var eq12 = new Equalizer(1); | |
590 // Sanity. | |
591 Expect.equals(eq01, eq02); | |
592 Expect.equals(eq02, eq01); | |
593 Expect.equals(eq11, eq12); | |
594 Expect.equals(eq12, eq11); | |
595 Expect.notEquals(eq01, eq11); | |
596 Expect.notEquals(eq01, eq12); | |
597 Expect.notEquals(eq02, eq11); | |
598 Expect.notEquals(eq02, eq12); | |
599 Expect.notEquals(eq11, eq01); | |
600 Expect.notEquals(eq11, eq02); | |
601 Expect.notEquals(eq12, eq01); | |
602 Expect.notEquals(eq12, eq02); | |
603 | |
604 map[eq01] = 0; | |
605 map[eq02] = 1; | |
606 map[eq11] = 2; | |
607 map[eq12] = 3; | |
608 testLength(4, map); | |
609 | |
610 Expect.equals(0, map[eq01]); | |
611 Expect.equals(1, map[eq02]); | |
612 Expect.equals(2, map[eq11]); | |
613 Expect.equals(3, map[eq12]); | |
614 | |
615 Expect.isTrue(map.containsKey(eq01)); | |
616 Expect.isTrue(map.containsKey(eq02)); | |
617 Expect.isTrue(map.containsKey(eq11)); | |
618 Expect.isTrue(map.containsKey(eq12)); | |
619 | |
620 Expect.equals(1, map.remove(eq02)); | |
621 Expect.equals(3, map.remove(eq12)); | |
622 testLength(2, map); | |
623 Expect.isTrue(map.containsKey(eq01)); | |
624 Expect.isFalse(map.containsKey(eq02)); | |
625 Expect.isTrue(map.containsKey(eq11)); | |
626 Expect.isFalse(map.containsKey(eq12)); | |
627 | |
628 Expect.equals(0, map[eq01]); | |
629 Expect.equals(null, map[eq02]); | |
630 Expect.equals(2, map[eq11]); | |
631 Expect.equals(null, map[eq12]); | |
632 | |
633 Expect.equals(0, map.remove(eq01)); | |
634 Expect.equals(2, map.remove(eq11)); | |
635 testLength(0, map); | |
636 | |
637 map[eq01] = 0; | |
638 map[eq02] = 1; | |
639 map[eq11] = 2; | |
640 map[eq12] = 3; | |
641 testLength(4, map); | |
642 | |
643 // Transfer to equality-based map will collapse elements. | |
644 Map eqMap = new HashMap(); | |
645 eqMap.addAll(map); | |
646 testLength(2, eqMap); | |
647 Expect.isTrue(eqMap.containsKey(eq01)); | |
648 Expect.isTrue(eqMap.containsKey(eq02)); | |
649 Expect.isTrue(eqMap.containsKey(eq11)); | |
650 Expect.isTrue(eqMap.containsKey(eq12)); | |
651 | |
652 // Changing objects will not affect identity map. | |
653 map.clear(); | |
654 var m1 = new Mutable(1); | |
655 var m2 = new Mutable(2); | |
656 var m3 = new Mutable(3); | |
657 map[m1] = 1; | |
658 map[m2] = 2; | |
659 map[m3] = 3; | |
660 Expect.equals(3, map.length); | |
661 Expect.isTrue(map.containsKey(m1)); | |
662 Expect.isTrue(map.containsKey(m2)); | |
663 Expect.isTrue(map.containsKey(m3)); | |
664 Expect.notEquals(m1, m3); | |
665 m3.id = 1; | |
666 Expect.equals(m1, m3); | |
667 // Even if keys are equal, they are still not identical. | |
668 // Even if hashcode of m3 changed, it can still be found. | |
669 Expect.equals(1, map[m1]); | |
670 Expect.equals(3, map[m3]); | |
671 } | |
672 | |
673 /** Class of objects that are equal if they hold the same id. */ | |
674 class Equalizer { | |
675 int id; | |
676 Equalizer(this.id); | |
677 int get hashCode => id; | |
678 bool operator ==(Object other) => | |
679 other is Equalizer && id == (other as Equalizer).id; | |
680 } | |
681 | |
682 /** | |
683 * Objects that are not reflexive. | |
684 * | |
685 * They think they are better than their equals. | |
686 */ | |
687 class Vampire { | |
688 final int generation; | |
689 const Vampire(this.generation); | |
690 | |
691 int get hashCode => generation; | |
692 | |
693 // The double-fang operator falsely claims that a vampire is equal to | |
694 // any of its sire's generation. | |
695 bool operator ==(Object other) => | |
696 other is Vampire && generation - 1 == (other as Vampire).generation; | |
697 } | |
698 | |
699 void testCustomMap(Map map) { | |
700 testLength(0, map); | |
701 var c11 = const Customer(1, 1); | |
702 var c12 = const Customer(1, 2); | |
703 var c21 = const Customer(2, 1); | |
704 var c22 = const Customer(2, 2); | |
705 // Sanity. | |
706 Expect.equals(c11, c12); | |
707 Expect.notEquals(c11, c21); | |
708 Expect.notEquals(c11, c22); | |
709 Expect.equals(c21, c22); | |
710 Expect.notEquals(c21, c11); | |
711 Expect.notEquals(c21, c12); | |
712 | |
713 Expect.isTrue(myEquals(c11, c21)); | |
714 Expect.isFalse(myEquals(c11, c12)); | |
715 Expect.isFalse(myEquals(c11, c22)); | |
716 Expect.isTrue(myEquals(c12, c22)); | |
717 Expect.isFalse(myEquals(c12, c11)); | |
718 Expect.isFalse(myEquals(c12, c21)); | |
719 | |
720 map[c11] = 42; | |
721 testLength(1, map); | |
722 Expect.isTrue(map.containsKey(c11)); | |
723 Expect.isTrue(map.containsKey(c21)); | |
724 Expect.isFalse(map.containsKey(c12)); | |
725 Expect.isFalse(map.containsKey(c22)); | |
726 Expect.equals(42, map[c11]); | |
727 Expect.equals(42, map[c21]); | |
728 | |
729 map[c21] = 37; | |
730 testLength(1, map); | |
731 Expect.isTrue(map.containsKey(c11)); | |
732 Expect.isTrue(map.containsKey(c21)); | |
733 Expect.isFalse(map.containsKey(c12)); | |
734 Expect.isFalse(map.containsKey(c22)); | |
735 Expect.equals(37, map[c11]); | |
736 Expect.equals(37, map[c21]); | |
737 | |
738 map[c22] = 42; | |
739 testLength(2, map); | |
740 Expect.isTrue(map.containsKey(c11)); | |
741 Expect.isTrue(map.containsKey(c21)); | |
742 Expect.isTrue(map.containsKey(c12)); | |
743 Expect.isTrue(map.containsKey(c22)); | |
744 Expect.equals(37, map[c11]); | |
745 Expect.equals(37, map[c21]); | |
746 Expect.equals(42, map[c12]); | |
747 Expect.equals(42, map[c22]); | |
748 | |
749 Expect.equals(42, map.remove(c12)); | |
750 testLength(1, map); | |
751 Expect.isTrue(map.containsKey(c11)); | |
752 Expect.isTrue(map.containsKey(c21)); | |
753 Expect.isFalse(map.containsKey(c12)); | |
754 Expect.isFalse(map.containsKey(c22)); | |
755 Expect.equals(37, map[c11]); | |
756 Expect.equals(37, map[c21]); | |
757 | |
758 Expect.equals(37, map.remove(c11)); | |
759 testLength(0, map); | |
760 } | |
761 | |
762 void testUnmodifiableMap(Map map) { | |
763 Expect.isTrue(map.containsKey(1)); | |
764 testLength(1, map); | |
765 Expect.equals(1, map.keys.first); | |
766 Expect.equals(37, map.values.first); | |
767 | |
768 Expect.throws(map.clear); | |
769 Expect.throws(() { | |
770 map.remove(1); | |
771 }); | |
772 Expect.throws(() { | |
773 map[2] = 42; | |
774 }); | |
775 Expect.throws(() { | |
776 map.addAll({2: 42}); | |
777 }); | |
778 } | |
779 | |
780 class Customer { | |
781 final int id; | |
782 final int secondId; | |
783 const Customer(this.id, this.secondId); | |
784 int get hashCode => id; | |
785 bool operator ==(Object other) { | |
786 if (other is! Customer) return false; | |
787 Customer otherCustomer = other; | |
788 return id == otherCustomer.id; | |
789 } | |
790 } | |
791 | |
792 int myHashCode(Customer c) => c.secondId; | |
793 bool myEquals(Customer a, Customer b) => a.secondId == b.secondId; | |
794 | |
795 void testIterationOrder(Map map) { | |
796 var order = [0, 6, 4, 2, 7, 9, 7, 1, 2, 5, 3]; | |
797 for (int i = 0; i < order.length; i++) map[order[i]] = i; | |
798 Expect.listEquals(map.keys.toList(), [0, 6, 4, 2, 7, 9, 1, 5, 3]); | |
799 Expect.listEquals(map.values.toList(), [0, 1, 2, 8, 6, 5, 7, 9, 10]); | |
800 } | |
801 | |
802 void testOtherKeys(Map<int, int> map) { | |
803 // Test that non-int keys are allowed in containsKey/remove/lookup. | |
804 // Custom hash sets and tree sets must be constructed so they don't | |
805 // use the equality/comparator on incompatible objects. | |
806 | |
807 // This should not throw in either checked or unchecked mode. | |
808 map[0] = 0; | |
809 map[1] = 1; | |
810 map[2] = 2; | |
811 Expect.isFalse(map.containsKey("not an int")); | |
812 Expect.isFalse(map.containsKey(1.5)); | |
813 Expect.isNull(map.remove("not an int")); | |
814 Expect.isNull(map.remove(1.5)); | |
815 Expect.isNull(map["not an int"]); | |
816 Expect.isNull(map[1.5]); | |
817 } | |
818 | |
819 class Mutable { | |
820 int id; | |
821 Mutable(this.id); | |
822 int get hashCode => id; | |
823 bool operator ==(other) => other is Mutable && other.id == id; | |
824 } | |
825 | |
826 // Slow implementation of Map based on MapBase. | |
827 abstract class MapBaseOperations<K, V> { | |
828 final List _keys = <K>[]; | |
829 final List _values = <V>[]; | |
830 int _modCount = 0; | |
831 | |
832 V operator [](Object key) { | |
833 int index = _keys.indexOf(key); | |
834 if (index < 0) return null; | |
835 return _values[index]; | |
836 } | |
837 | |
838 Iterable<K> get keys => new TestKeyIterable<K>(this); | |
839 | |
840 void operator []=(K key, V value) { | |
841 int index = _keys.indexOf(key); | |
842 if (index >= 0) { | |
843 _values[index] = value; | |
844 } else { | |
845 _modCount++; | |
846 _keys.add(key); | |
847 _values.add(value); | |
848 } | |
849 } | |
850 | |
851 V remove(Object key) { | |
852 int index = _keys.indexOf(key); | |
853 if (index >= 0) { | |
854 var result = _values[index]; | |
855 key = _keys.removeLast(); | |
856 var value = _values.removeLast(); | |
857 if (index != _keys.length) { | |
858 _keys[index] = key; | |
859 _values[index] = value; | |
860 } | |
861 _modCount++; | |
862 return result; | |
863 } | |
864 return null; | |
865 } | |
866 | |
867 void clear() { | |
868 // Clear cannot be based on remove, since remove won't remove keys that | |
869 // are not equal to themselves. It will fail the testNaNKeys test. | |
870 _keys.clear(); | |
871 _values.clear(); | |
872 _modCount++; | |
873 } | |
874 } | |
875 | |
876 class MapBaseMap<K, V> = MapBase<K, V> with MapBaseOperations<K, V>; | |
877 class MapMixinMap<K, V> = MapBaseOperations<K, V> with MapMixin<K, V>; | |
878 | |
879 class TestKeyIterable<K> extends IterableBase<K> { | |
880 final _map; | |
881 TestKeyIterable(this._map); | |
882 int get length => _map._keys.length; | |
883 Iterator<K> get iterator => new TestKeyIterator<K>(_map); | |
884 } | |
885 | |
886 class TestKeyIterator<K> implements Iterator<K> { | |
887 final _map; | |
888 final int _modCount; | |
889 int _index = 0; | |
890 var _current; | |
891 TestKeyIterator(map) | |
892 : _map = map, | |
893 _modCount = map._modCount; | |
894 bool moveNext() { | |
895 if (_modCount != _map._modCount) { | |
896 throw new ConcurrentModificationError(_map); | |
897 } | |
898 if (_index == _map._keys.length) { | |
899 _current = null; | |
900 return false; | |
901 } | |
902 _current = _map._keys[_index++]; | |
903 return true; | |
904 } | |
905 | |
906 K get current => _current; | |
907 } | |
908 | |
909 // Slow implementation of Map based on MapBase. | |
910 class UnmodifiableMapBaseMap<K, V> extends UnmodifiableMapBase<K, V> { | |
911 final List _keys = <K>[]; | |
912 final List _values = <V>[]; | |
913 UnmodifiableMapBaseMap(List pairs) { | |
914 for (int i = 0; i < pairs.length; i += 2) { | |
915 _keys.add(pairs[i]); | |
916 _values.add(pairs[i + 1]); | |
917 } | |
918 } | |
919 | |
920 int get _modCount => 0; | |
921 | |
922 V operator [](K key) { | |
923 int index = _keys.indexOf(key); | |
924 if (index < 0) return null; | |
925 return _values[index]; | |
926 } | |
927 | |
928 Iterable<K> get keys => _keys.skip(0); | |
929 } | |
930 | |
931 abstract class Super implements Comparable {} | |
932 | |
933 abstract class Interface implements Comparable {} | |
934 | |
935 class Sub extends Super implements Interface, Comparable { | |
936 int compareTo(Sub other) => 0; | |
937 int get hashCode => 0; | |
938 bool operator ==(other) => other is Sub; | |
939 } | |
940 | |
941 expectMap(Map expect, Map actual) { | |
942 Expect.equals(expect.length, actual.length, "length"); | |
943 for (var key in expect.keys) { | |
944 Expect.isTrue(actual.containsKey(key), "containsKey $key"); | |
945 Expect.equals(expect[key], actual[key]); | |
946 } | |
947 } | |
948 | |
949 void testFrom() { | |
950 // Check contents. | |
951 for (var map in [ | |
952 {}, | |
953 {1: 1}, | |
954 {1: 2, 3: 4, 5: 6, 7: 8} | |
955 ]) { | |
956 expectMap(map, new Map.from(map)); | |
957 expectMap(map, new HashMap.from(map)); | |
958 expectMap(map, new LinkedHashMap.from(map)); | |
959 expectMap(map, new SplayTreeMap.from(map)); | |
960 } | |
961 // Test type combinations allowed. | |
962 Map<int, int> intMap = <int, int>{1: 2, 3: 4}; | |
963 Map<num, num> numMap = <num, num>{1: 2, 3: 4}; | |
964 expectMap(intMap, new Map<int, int>.from(numMap)); | |
965 expectMap(intMap, new Map<num, num>.from(intMap)); | |
966 expectMap(intMap, new HashMap<int, int>.from(numMap)); | |
967 expectMap(intMap, new HashMap<num, num>.from(intMap)); | |
968 expectMap(intMap, new LinkedHashMap<int, int>.from(numMap)); | |
969 expectMap(intMap, new LinkedHashMap<num, num>.from(intMap)); | |
970 expectMap(intMap, new SplayTreeMap<int, int>.from(numMap)); | |
971 expectMap(intMap, new SplayTreeMap<num, num>.from(intMap)); | |
972 | |
973 var sub = new Sub(); | |
974 Map<Super, Super> superMap = <Super, Super>{sub: sub}; | |
975 Map<Interface, Interface> interfaceMap = <Interface, Interface>{sub: sub}; | |
976 expectMap(superMap, new Map<Super, Super>.from(interfaceMap)); | |
977 expectMap(superMap, new Map<Interface, Interface>.from(superMap)); | |
978 expectMap(superMap, new HashMap<Super, Super>.from(interfaceMap)); | |
979 expectMap(superMap, new HashMap<Interface, Interface>.from(superMap)); | |
980 expectMap(superMap, new LinkedHashMap<Super, Super>.from(interfaceMap)); | |
981 expectMap(superMap, new LinkedHashMap<Interface, Interface>.from(superMap)); | |
982 expectMap(superMap, new SplayTreeMap<Super, Super>.from(interfaceMap)); | |
983 expectMap(superMap, new SplayTreeMap<Interface, Interface>.from(superMap)); | |
984 } | |
OLD | NEW |