OLD | NEW |
| (Empty) |
1 // Copyright (c) 2013, 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 // Tests of hash map behavior, with focus in iteration and concurrent | |
6 // modification errors. | |
7 | |
8 library hash_map2_test; | |
9 import 'dart:collection'; | |
10 | |
11 testMap(Map newMap(), Map newMapFrom(Map map)) { | |
12 Map gen(int from, int to) { | |
13 Map map = new LinkedHashMap(); | |
14 for (int i = from; i < to; i++) map[i] = i; | |
15 return map; | |
16 } | |
17 | |
18 bool odd(int n) => (n & 1) == 1; | |
19 bool even(int n) => (n & 1) == 0; | |
20 void addAll(Map toMap, Map fromMap) { | |
21 fromMap.forEach((k, v) { toMap[k] = v; }); | |
22 } | |
23 | |
24 { // Test growing to largish capacity. | |
25 Map map = newMap(); | |
26 | |
27 for (int i = 0; i < 256; i++) { | |
28 map[i] = i; | |
29 } | |
30 addAll(map, gen(256, 512)); | |
31 addAll(map, newMapFrom(gen(512, 1000))); | |
32 Expect.equals(1000, map.length); | |
33 | |
34 // Remove half. | |
35 for (int i = 0; i < 1000; i += 2) map.remove(i); | |
36 Expect.equals(500, map.length); | |
37 Expect.isFalse(map.keys.any(even)); | |
38 Expect.isTrue(map.keys.every(odd)); | |
39 | |
40 // Re-add all. | |
41 addAll(map, gen(0, 1000)); | |
42 Expect.equals(1000, map.length); | |
43 } | |
44 | |
45 { // Test having many deleted elements. | |
46 Map map = newMap(); | |
47 map[0] = 0; | |
48 for (int i = 0; i < 1000; i++) { | |
49 map[i + 1] = i + 1; | |
50 map.remove(i); | |
51 Expect.equals(1, map.length); | |
52 } | |
53 } | |
54 | |
55 { // Test having many elements with same hashCode | |
56 Map map = newMap(); | |
57 for (int i = 0; i < 1000; i++) { | |
58 map[new BadHashCode()] = 0; | |
59 } | |
60 Expect.equals(1000, map.length); | |
61 } | |
62 | |
63 { // Check concurrent modification | |
64 Map map = newMap()..[0] = 0..[1] = 1; | |
65 | |
66 { // Test adding before a moveNext. | |
67 Iterator iter = map.keys.iterator; | |
68 iter.moveNext(); | |
69 map[1] = 9; // Updating existing key isn't a modification. | |
70 iter.moveNext(); | |
71 map[2] = 2; | |
72 Expect.throws(iter.moveNext, (e) => e is Error); | |
73 } | |
74 | |
75 { // Test adding after last element. | |
76 Iterator iter = map.keys.iterator; | |
77 Expect.equals(3, map.length); | |
78 iter.moveNext(); | |
79 iter.moveNext(); | |
80 iter.moveNext(); | |
81 map[3] = 3; | |
82 Expect.throws(iter.moveNext, (e) => e is Error); | |
83 } | |
84 | |
85 { // Test removing during iteration. | |
86 Iterator iter = map.keys.iterator; | |
87 iter.moveNext(); | |
88 map.remove(1000); // Not a modification if it's not there. | |
89 iter.moveNext(); | |
90 int n = iter.current; | |
91 map.remove(n); | |
92 // Removing doesn't change current. | |
93 Expect.equals(n, iter.current); | |
94 Expect.throws(iter.moveNext, (e) => e is Error); | |
95 } | |
96 | |
97 { // Test removing after last element. | |
98 Iterator iter = map.keys.iterator; | |
99 Expect.equals(3, map.length); | |
100 iter.moveNext(); | |
101 iter.moveNext(); | |
102 iter.moveNext(); | |
103 int n = iter.current; | |
104 map.remove(n); | |
105 // Removing doesn't change current. | |
106 Expect.equals(n, iter.current); | |
107 Expect.throws(iter.moveNext, (e) => e is Error); | |
108 } | |
109 | |
110 { // Test that updating value doesn't cause error. | |
111 Iterator iter = map.keys.iterator; | |
112 Expect.equals(2, map.length); | |
113 iter.moveNext(); | |
114 int n = iter.current; | |
115 map[n] = n * 2; | |
116 iter.moveNext(); | |
117 Expect.equals(map[iter.current], iter.current); | |
118 } | |
119 | |
120 { // Test that modification during putIfAbsent is error | |
121 Expect.throws(() => map.putIfAbsent(4, () { | |
122 map[5] = 5; | |
123 return 4; | |
124 }), (e) => e is Error); | |
125 Expect.isFalse(map.containsKey(4)); | |
126 Expect.isTrue(map.containsKey(5)); | |
127 } | |
128 | |
129 { // Check adding many existing keys isn't considered modification. | |
130 Map map2 = newMap(); | |
131 for (var key in map.keys) { | |
132 map2[key] = map[key] + 1; | |
133 } | |
134 Iterator iter = map.keys.iterator; | |
135 addAll(map, map2); | |
136 // Shouldn't throw. | |
137 iter.moveNext(); | |
138 } | |
139 } | |
140 | |
141 { // Check that updating existing elements is not a modification. | |
142 // This must be the case even if the underlying data structure is | |
143 // nearly full. | |
144 for (int i = 1; i < 128; i++) { | |
145 // Create maps of different sizes, some of which should be | |
146 // at a limit of the underlying data structure. | |
147 Map map = newMapFrom(gen(0, i)); | |
148 Iterator iter = map.keys.iterator; | |
149 for (int j = 0; j < i; j++) { | |
150 map[j] = j + 1; | |
151 } | |
152 iter.moveNext(); // Should not throw. | |
153 | |
154 for (int j = 1; j < i; j++) { | |
155 map.remove(j); | |
156 } | |
157 iter = map.keys.iterator; | |
158 map[0] = 2; | |
159 iter.moveNext(); // Should not throw. | |
160 } | |
161 } | |
162 | |
163 { // Check that null can be in the map. | |
164 Map map = newMap(); | |
165 map[null] = 0; | |
166 Expect.equals(1, map.length); | |
167 Expect.isTrue(map.containsKey(null)); | |
168 Expect.isNull(map.keys.first); | |
169 Expect.isNull(map.keys.last); | |
170 map[null] = 1; | |
171 Expect.equals(1, map.length); | |
172 Expect.isTrue(map.containsKey(null)); | |
173 map.remove(null); | |
174 Expect.isTrue(map.isEmpty); | |
175 Expect.isFalse(map.containsKey(null)); | |
176 | |
177 // Created using map.from. | |
178 map = newMapFrom(new Map()..[null] = 0); | |
179 Expect.equals(1, map.length); | |
180 Expect.isTrue(map.containsKey(null)); | |
181 Expect.isNull(map.keys.first); | |
182 Expect.isNull(map.keys.last); | |
183 map[null] = 1; | |
184 Expect.equals(1, map.length); | |
185 Expect.isTrue(map.containsKey(null)); | |
186 map.remove(null); | |
187 Expect.isTrue(map.isEmpty); | |
188 Expect.isFalse(map.containsKey(null)); | |
189 | |
190 Map fromMap = new Map(); | |
191 fromMap[1] = 0; | |
192 fromMap[2] = 0; | |
193 fromMap[3] = 0; | |
194 fromMap[null] = 0; | |
195 fromMap[4] = 0; | |
196 fromMap[5] = 0; | |
197 fromMap[6] = 0; | |
198 Expect.equals(7, fromMap.length); | |
199 | |
200 // map that grows with null in it. | |
201 map = newMapFrom(fromMap); | |
202 Expect.equals(7, map.length); | |
203 for (int i = 7; i < 128; i++) { | |
204 map[i] = 0; | |
205 } | |
206 Expect.equals(128, map.length); | |
207 Expect.isTrue(map.containsKey(null)); | |
208 map[null] = 1; | |
209 Expect.equals(128, map.length); | |
210 Expect.isTrue(map.containsKey(null)); | |
211 map.remove(null); | |
212 Expect.equals(127, map.length); | |
213 Expect.isFalse(map.containsKey(null)); | |
214 } | |
215 } | |
216 | |
217 void main() { | |
218 testMap(() => new HashMap(), (m) => new HashMap.from(m)); | |
219 testMap(() => new LinkedHashMap(), (m) => new LinkedHashMap.from(m)); | |
220 } | |
221 | |
222 | |
223 class BadHashCode { | |
224 static int idCounter = 0; | |
225 final int id; | |
226 BadHashCode() : id = idCounter++; | |
227 int get hashCode => 42; | |
228 // operator == is identity. | |
229 // Can't make a bad compareTo that isn't invalid. | |
230 int compareTo(BadHashCode other) => id - other.id; | |
231 } | |
OLD | NEW |