| 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 // VMOptions= | |
| 6 | |
| 7 // Tests of hash set behavior, with focus in iteration and concurrent | |
| 8 // modification errors. | |
| 9 | |
| 10 library hash_map2_test; | |
| 11 | |
| 12 import "package:expect/expect.dart"; | |
| 13 import 'dart:collection'; | |
| 14 import 'dart:math' as math; | |
| 15 | |
| 16 testSet(Set newSet(), Set newSetFrom(Iterable from)) { | |
| 17 Set gen(int from, int to) => | |
| 18 new Set.from(new Iterable.generate(to - from, (n) => n + from)); | |
| 19 | |
| 20 bool odd(int n) => (n & 1) == 1; | |
| 21 bool even(int n) => (n & 1) == 0; | |
| 22 | |
| 23 { | |
| 24 // Test growing to largish capacity. | |
| 25 Set set = newSet(); | |
| 26 | |
| 27 for (int i = 0; i < 256; i++) { | |
| 28 set.add(i); | |
| 29 } | |
| 30 | |
| 31 set.addAll(gen(256, 512)); | |
| 32 set.addAll(newSetFrom(gen(512, 1000))); | |
| 33 Expect.equals(1000, set.length); | |
| 34 | |
| 35 // Remove half. | |
| 36 for (int i = 0; i < 1000; i += 2) set.remove(i); | |
| 37 Expect.equals(500, set.length); | |
| 38 Expect.isFalse(set.any(even)); | |
| 39 Expect.isTrue(set.every(odd)); | |
| 40 | |
| 41 // Re-add all. | |
| 42 set.addAll(gen(0, 1000)); | |
| 43 Expect.equals(1000, set.length); | |
| 44 } | |
| 45 | |
| 46 { | |
| 47 // Test having many deleted elements. | |
| 48 Set set = newSet(); | |
| 49 set.add(0); | |
| 50 for (int i = 0; i < 1000; i++) { | |
| 51 set.add(i + 1); | |
| 52 set.remove(i); | |
| 53 Expect.equals(1, set.length); | |
| 54 } | |
| 55 } | |
| 56 | |
| 57 { | |
| 58 // Test having many elements with same hashCode | |
| 59 Set set = newSet(); | |
| 60 for (int i = 0; i < 1000; i++) { | |
| 61 set.add(new BadHashCode()); | |
| 62 } | |
| 63 Expect.equals(1000, set.length); | |
| 64 } | |
| 65 | |
| 66 { | |
| 67 // Check concurrent modification | |
| 68 Set set = newSet()..add(0)..add(1); | |
| 69 | |
| 70 { | |
| 71 // Test adding before a moveNext. | |
| 72 Iterator iter = set.iterator; | |
| 73 iter.moveNext(); | |
| 74 set.add(1); // Updating existing key isn't a modification. | |
| 75 iter.moveNext(); | |
| 76 set.add(2); | |
| 77 Expect.throws(iter.moveNext, (e) => e is Error); | |
| 78 } | |
| 79 | |
| 80 { | |
| 81 // Test adding after last element. | |
| 82 Iterator iter = set.iterator; | |
| 83 Expect.equals(3, set.length); | |
| 84 iter.moveNext(); | |
| 85 iter.moveNext(); | |
| 86 iter.moveNext(); | |
| 87 set.add(3); | |
| 88 Expect.throws(iter.moveNext, (e) => e is Error); | |
| 89 } | |
| 90 | |
| 91 { | |
| 92 // Test removing during iteration. | |
| 93 Iterator iter = set.iterator; | |
| 94 iter.moveNext(); | |
| 95 set.remove(1000); // Not a modification if it's not there. | |
| 96 iter.moveNext(); | |
| 97 int n = iter.current; | |
| 98 set.remove(n); | |
| 99 // Removing doesn't change current. | |
| 100 Expect.equals(n, iter.current); | |
| 101 Expect.throws(iter.moveNext, (e) => e is Error); | |
| 102 } | |
| 103 | |
| 104 { | |
| 105 // Test removing after last element. | |
| 106 Iterator iter = set.iterator; | |
| 107 Expect.equals(3, set.length); | |
| 108 iter.moveNext(); | |
| 109 iter.moveNext(); | |
| 110 iter.moveNext(); | |
| 111 int n = iter.current; | |
| 112 set.remove(n); | |
| 113 // Removing doesn't change current. | |
| 114 Expect.equals(n, iter.current); | |
| 115 Expect.throws(iter.moveNext, (e) => e is Error); | |
| 116 } | |
| 117 | |
| 118 { | |
| 119 // Test that updating value doesn't cause error. | |
| 120 Iterator iter = set.iterator; | |
| 121 Expect.equals(2, set.length); | |
| 122 iter.moveNext(); | |
| 123 int n = iter.current; | |
| 124 set.add(n); | |
| 125 iter.moveNext(); | |
| 126 Expect.isTrue(set.contains(iter.current)); | |
| 127 } | |
| 128 | |
| 129 { | |
| 130 // Check adding many existing values isn't considered modification. | |
| 131 Set set2 = newSet(); | |
| 132 for (var value in set) { | |
| 133 set2.add(value); | |
| 134 } | |
| 135 Iterator iter = set.iterator; | |
| 136 set.addAll(set2); | |
| 137 // Shouldn't throw. | |
| 138 iter.moveNext(); | |
| 139 } | |
| 140 } | |
| 141 | |
| 142 { | |
| 143 // Check that updating existing elements is not a modification. | |
| 144 // This must be the case even if the underlying data structure is | |
| 145 // nearly full. | |
| 146 for (int i = 1; i < 128; i++) { | |
| 147 // Create maps of different sizes, some of which should be | |
| 148 // at a limit of the underlying data structure. | |
| 149 Set set = newSetFrom(gen(0, i)); | |
| 150 Iterator iter = set.iterator; | |
| 151 for (int j = 0; j < i; j++) { | |
| 152 set.add(j); | |
| 153 } | |
| 154 iter.moveNext(); // Should not throw. | |
| 155 | |
| 156 for (int j = 1; j < i; j++) { | |
| 157 set.remove(j); | |
| 158 } | |
| 159 iter = set.iterator; | |
| 160 set.add(0); | |
| 161 iter.moveNext(); // Should not throw. | |
| 162 } | |
| 163 } | |
| 164 | |
| 165 { | |
| 166 // Check that null can be in the set. | |
| 167 Set set = newSet(); | |
| 168 set.add(null); | |
| 169 Expect.equals(1, set.length); | |
| 170 Expect.isTrue(set.contains(null)); | |
| 171 Expect.isNull(set.first); | |
| 172 Expect.isNull(set.last); | |
| 173 set.add(null); | |
| 174 Expect.equals(1, set.length); | |
| 175 Expect.isTrue(set.contains(null)); | |
| 176 set.remove(null); | |
| 177 Expect.isTrue(set.isEmpty); | |
| 178 Expect.isFalse(set.contains(null)); | |
| 179 | |
| 180 // Created using Set.from. | |
| 181 set = newSetFrom([null]); | |
| 182 Expect.equals(1, set.length); | |
| 183 Expect.isTrue(set.contains(null)); | |
| 184 Expect.isNull(set.first); | |
| 185 Expect.isNull(set.last); | |
| 186 set.add(null); | |
| 187 Expect.equals(1, set.length); | |
| 188 Expect.isTrue(set.contains(null)); | |
| 189 set.remove(null); | |
| 190 Expect.isTrue(set.isEmpty); | |
| 191 Expect.isFalse(set.contains(null)); | |
| 192 | |
| 193 // Set that grows with null in it. | |
| 194 set = newSetFrom([1, 2, 3, null, 4, 5, 6]); | |
| 195 Expect.equals(7, set.length); | |
| 196 for (int i = 7; i < 128; i++) { | |
| 197 set.add(i); | |
| 198 } | |
| 199 Expect.equals(128, set.length); | |
| 200 Expect.isTrue(set.contains(null)); | |
| 201 set.add(null); | |
| 202 Expect.equals(128, set.length); | |
| 203 Expect.isTrue(set.contains(null)); | |
| 204 set.remove(null); | |
| 205 Expect.equals(127, set.length); | |
| 206 Expect.isFalse(set.contains(null)); | |
| 207 } | |
| 208 | |
| 209 { | |
| 210 // Check that addAll and clear works. | |
| 211 Set set = newSet(); | |
| 212 set.addAll([]); | |
| 213 Expect.isTrue(set.isEmpty); | |
| 214 set.addAll([1, 3, 2]); | |
| 215 Expect.equals(3, set.length); | |
| 216 Expect.isTrue(set.contains(1)); | |
| 217 Expect.isTrue(set.contains(3)); | |
| 218 Expect.isTrue(set.contains(2)); | |
| 219 Expect.isFalse(set.contains(4)); | |
| 220 set.clear(); | |
| 221 Expect.isTrue(set.isEmpty); | |
| 222 } | |
| 223 | |
| 224 { | |
| 225 // Check that removeWhere and retainWhere work. | |
| 226 Set set = newSetFrom([1, 2, 3]); | |
| 227 set.removeWhere((each) => each == 2); | |
| 228 Expect.equals(2, set.length); | |
| 229 Expect.isTrue(set.contains(1)); | |
| 230 Expect.isFalse(set.contains(2)); | |
| 231 Expect.isTrue(set.contains(3)); | |
| 232 set.retainWhere((each) => each == 3); | |
| 233 Expect.equals(1, set.length); | |
| 234 Expect.isFalse(set.contains(1)); | |
| 235 Expect.isFalse(set.contains(2)); | |
| 236 Expect.isTrue(set.contains(3)); | |
| 237 } | |
| 238 | |
| 239 { | |
| 240 // Test lookup | |
| 241 Set set = newSet(); | |
| 242 var m1a = new Mutable(1); | |
| 243 var m1b = new Mutable(1); | |
| 244 var m2a = new Mutable(2); | |
| 245 var m2b = new Mutable(2); | |
| 246 Expect.isNull(set.lookup(m1a)); | |
| 247 Expect.isNull(set.lookup(m1b)); | |
| 248 set.add(m1a); | |
| 249 Expect.identical(m1a, set.lookup(m1a)); | |
| 250 Expect.identical(m1a, set.lookup(m1b)); | |
| 251 | |
| 252 Expect.isNull(set.lookup(m2a)); | |
| 253 Expect.isNull(set.lookup(m2b)); | |
| 254 set.add(m2a); | |
| 255 Expect.identical(m2a, set.lookup(m2a)); | |
| 256 Expect.identical(m2a, set.lookup(m2b)); | |
| 257 | |
| 258 set.add(m2b); // Adding doesn't change element. | |
| 259 Expect.identical(m2a, set.lookup(m2a)); | |
| 260 Expect.identical(m2a, set.lookup(m2b)); | |
| 261 | |
| 262 set.remove(m1a); | |
| 263 set.add(m1b); | |
| 264 Expect.identical(m1b, set.lookup(m1a)); | |
| 265 Expect.identical(m1b, set.lookup(m1b)); | |
| 266 | |
| 267 set.add(1); | |
| 268 Expect.identical(1, set.lookup(1.0)); | |
| 269 set.add(-0.0); | |
| 270 Expect.identical(-0.0, set.lookup(0.0)); | |
| 271 } | |
| 272 | |
| 273 { | |
| 274 // Test special hash codes | |
| 275 Set set = newSet(); | |
| 276 List keys = []; | |
| 277 // Powers of two | |
| 278 for (int i = 63; i >= 2; --i) { | |
| 279 keys.add(new Mutable(math.pow(2, i))); | |
| 280 } | |
| 281 for (var key in keys) { | |
| 282 Expect.isTrue(set.add(key)); | |
| 283 } | |
| 284 for (var key in keys) { | |
| 285 Expect.isTrue(set.contains(key)); | |
| 286 } | |
| 287 } | |
| 288 } | |
| 289 | |
| 290 void testIdentitySet(Set create()) { | |
| 291 Set set = create(); | |
| 292 set.add(1); | |
| 293 set.add(2); | |
| 294 set.add(1); // Integers are identical if equal. | |
| 295 Expect.equals(2, set.length); | |
| 296 var complex = 4; | |
| 297 complex = set.length == 2 ? complex ~/ 4 : 87; // Avoid compile-time constant. | |
| 298 Expect.isTrue(set.contains(complex)); // 1 is in set, even if computed. | |
| 299 set.clear(); | |
| 300 | |
| 301 // All compile time constants are identical to themselves. | |
| 302 var constants = [ | |
| 303 double.INFINITY, | |
| 304 double.NAN, -0.0, //# 01: ok | |
| 305 0.0, 42, "", null, false, true, #bif, testIdentitySet | |
| 306 ]; | |
| 307 set.addAll(constants); | |
| 308 Expect.equals(constants.length, set.length); | |
| 309 for (var c in constants) { | |
| 310 Expect.isTrue(set.contains(c), "constant: $c"); | |
| 311 } | |
| 312 Expect.isTrue(set.containsAll(constants), "constants: $set"); | |
| 313 set.clear(); | |
| 314 | |
| 315 var m1 = new Mutable(1); | |
| 316 var m2 = new Mutable(2); | |
| 317 var m3 = new Mutable(3); | |
| 318 var m4 = new Mutable(2); // Equal to m2, but not identical. | |
| 319 set.addAll([m1, m2, m3, m4]); | |
| 320 Expect.equals(4, set.length); | |
| 321 Expect.equals(3, m3.hashCode); | |
| 322 m3.id = 1; | |
| 323 Expect.equals(1, m3.hashCode); | |
| 324 // Changing hashCode doesn't affect lookup. | |
| 325 Expect.isTrue(set.contains(m3)); | |
| 326 Expect.isTrue(set.contains(m1)); | |
| 327 set.remove(m3); | |
| 328 Expect.isFalse(set.contains(m3)); | |
| 329 Expect.isTrue(set.contains(m1)); | |
| 330 | |
| 331 Expect.identical(m1, set.lookup(m1)); | |
| 332 Expect.identical(null, set.lookup(m3)); | |
| 333 } | |
| 334 | |
| 335 void main() { | |
| 336 testSet(() => new Set(), (m) => new Set.from(m)); | |
| 337 testSet(() => new HashSet(), (m) => new HashSet.from(m)); | |
| 338 testSet(() => new LinkedHashSet(), (m) => new LinkedHashSet.from(m)); | |
| 339 testIdentitySet(() => new Set.identity()); | |
| 340 testIdentitySet(() => new HashSet.identity()); | |
| 341 testIdentitySet(() => new LinkedHashSet.identity()); | |
| 342 testIdentitySet(() => new HashSet( | |
| 343 equals: (x, y) => identical(x, y), hashCode: (x) => identityHashCode(x))); | |
| 344 testIdentitySet(() => new LinkedHashSet( | |
| 345 equals: (x, y) => identical(x, y), hashCode: (x) => identityHashCode(x))); | |
| 346 } | |
| 347 | |
| 348 class BadHashCode { | |
| 349 static int idCounter = 0; | |
| 350 final int id; | |
| 351 BadHashCode() : id = idCounter++; | |
| 352 int get hashCode => 42; | |
| 353 // operator == is identity. | |
| 354 // Can't make a bad compareTo that isn't invalid. | |
| 355 int compareTo(BadHashCode other) => id - other.id; | |
| 356 } | |
| 357 | |
| 358 class Mutable { | |
| 359 int id; | |
| 360 Mutable(this.id); | |
| 361 int get hashCode => id; | |
| 362 bool operator ==(other) => other is Mutable && id == other.id; | |
| 363 } | |
| OLD | NEW |