OLD | NEW |
(Empty) | |
| 1 // Copyright 2013 Google Inc. All Rights Reserved. |
| 2 // |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 // you may not use this file except in compliance with the License. |
| 5 // You may obtain a copy of the License at |
| 6 // |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 // |
| 9 // Unless required by applicable law or agreed to in writing, software |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 // See the License for the specific language governing permissions and |
| 13 // limitations under the License. |
| 14 |
| 15 library quiver.collection.treeset_test; |
| 16 |
| 17 import 'package:quiver/collection.dart'; |
| 18 import 'package:test/test.dart'; |
| 19 |
| 20 main() { |
| 21 group("TreeSet", () { |
| 22 group("when empty", () { |
| 23 TreeSet<num> tree; |
| 24 setUp(() { |
| 25 tree = new TreeSet<num>(); |
| 26 }); |
| 27 test("should actually be empty", () => expect(tree, isEmpty)); |
| 28 test("should not contain an element", |
| 29 () => expect(tree.lookup(0), isNull)); |
| 30 test("has no element when iterating forward", () { |
| 31 var i = tree.iterator; |
| 32 expect(i.moveNext(), isFalse, reason: "moveNext reports an element"); |
| 33 expect(i.current, isNull, reason: "current returns an element"); |
| 34 }); |
| 35 test("has no element when iterating backward", () { |
| 36 var i = tree.iterator; |
| 37 expect(i.movePrevious(), isFalse, |
| 38 reason: "movePrevious reports an element"); |
| 39 expect(i.current, isNull, reason: "current returns an element"); |
| 40 }); |
| 41 }); |
| 42 |
| 43 group("with [10, 20, 15]", () { |
| 44 AvlTreeSet<num> tree; |
| 45 setUp(() { |
| 46 tree = new TreeSet<num>()..addAll([10, 20, 15]); |
| 47 }); |
| 48 test("lookup succeeds for inserted elements", () { |
| 49 expect(tree.lookup(10), equals(10), reason: "missing 10"); |
| 50 expect(tree.lookup(15), equals(15), reason: "missing 15"); |
| 51 expect(tree.lookup(20), equals(20), reason: "missing 20"); |
| 52 }); |
| 53 test("order is correct", () { |
| 54 AvlNode ten = tree.getNode(10); |
| 55 AvlNode twenty = tree.getNode(20); |
| 56 AvlNode fifteen = tree.getNode(15); |
| 57 expect(ten.predecessor, isNull, reason: "10 is the smalled element"); |
| 58 expect(ten.successor, equals(fifteen), reason: "15 should follow 10"); |
| 59 expect(ten.successor.successor, equals(twenty), |
| 60 reason: "20 should follow 10"); |
| 61 |
| 62 expect(twenty.successor, isNull, reason: "20 is the largest element"); |
| 63 expect(twenty.predecessor, equals(fifteen), reason: "15 is before 20"); |
| 64 expect(twenty.predecessor.predecessor, equals(ten), |
| 65 reason: "10 is before 15"); |
| 66 }); |
| 67 }); |
| 68 |
| 69 group("with repeated elements", () { |
| 70 TreeSet<num> tree; |
| 71 setUp(() { |
| 72 tree = new TreeSet<num>()..addAll([10, 20, 15, 21, 30, 20]); |
| 73 }); |
| 74 |
| 75 test("only contains subset", () { |
| 76 var it = tree.iterator; |
| 77 var testList = new List.from([10, 15, 20, 21, 30]); |
| 78 while (it.moveNext()) { |
| 79 expect(it.current, equals(testList.removeAt(0))); |
| 80 } |
| 81 expect(testList.length, equals(0), reason: "valid subset seen in tree"); |
| 82 }); |
| 83 }); |
| 84 |
| 85 group("iteration", () { |
| 86 TreeSet<num> tree; |
| 87 setUp(() { |
| 88 tree = new TreeSet<num>()..addAll([10, 20, 15, 21, 30]); |
| 89 }); |
| 90 |
| 91 test("works bidirectionally", () { |
| 92 var it = tree.iterator; |
| 93 while (it.moveNext()); |
| 94 expect(it.movePrevious(), isTrue, |
| 95 reason: "we can backup after walking the entire list"); |
| 96 expect(it.current, equals(30), |
| 97 reason: "the last element is what we expect"); |
| 98 while (it.movePrevious()); |
| 99 expect(it.moveNext(), isTrue, |
| 100 reason: "we can move next after walking to the front of the set"); |
| 101 expect(it.current, equals(10), |
| 102 reason: "the first element is what we expect"); |
| 103 }); |
| 104 |
| 105 group("from", () { |
| 106 test("non-inserted midpoint works forward", () { |
| 107 var it = tree.fromIterator(19); |
| 108 expect(it.current, isNull, reason: "iteration starts with null"); |
| 109 expect(it.moveNext(), isTrue, reason: "moveNext() from spot works"); |
| 110 expect(it.current, equals(20)); |
| 111 }); |
| 112 |
| 113 test("non-inserted midpoint works for movePrevious()", () { |
| 114 var it = tree.fromIterator(19); |
| 115 expect(it.current, isNull, reason: "iteration starts with null"); |
| 116 expect(it.movePrevious(), isTrue, |
| 117 reason: "movePrevious() from spot works"); |
| 118 expect(it.current, equals(15)); |
| 119 }); |
| 120 |
| 121 test("non-inserted midpoint works reversed", () { |
| 122 var it = tree.fromIterator(19, reversed: true); |
| 123 expect(it.current, isNull, reason: "iteration starts with null"); |
| 124 expect(it.moveNext(), isTrue, reason: "moveNext() from spot works"); |
| 125 expect(it.current, equals(15)); |
| 126 }); |
| 127 |
| 128 test("non-inserted midpoint works reversed, movePrevious()", () { |
| 129 var it = tree.fromIterator(19, reversed: true); |
| 130 expect(it.current, isNull, reason: "iteration starts with null"); |
| 131 expect(it.movePrevious(), isTrue, |
| 132 reason: "movePrevious() from spot works"); |
| 133 expect(it.current, equals(20)); |
| 134 }); |
| 135 |
| 136 test("inserted midpoint works forward", () { |
| 137 var it = tree.fromIterator(20); |
| 138 expect(it.current, isNull, reason: "iteration starts with null"); |
| 139 expect(it.moveNext(), isTrue, reason: "moveNext() from spot works"); |
| 140 expect(it.current, equals(20)); |
| 141 }); |
| 142 |
| 143 test("inserted midpoint works reversed", () { |
| 144 var it = tree.fromIterator(20, reversed: true); |
| 145 expect(it.current, isNull, reason: "iteration starts with null"); |
| 146 expect(it.moveNext(), isTrue, reason: "moveNext() from spot works"); |
| 147 expect(it.current, equals(20)); |
| 148 }); |
| 149 |
| 150 test("after the set", () { |
| 151 var it = tree.fromIterator(100); |
| 152 expect(it.current, isNull); |
| 153 expect(it.moveNext(), isFalse, reason: "not following items"); |
| 154 expect(it.movePrevious(), isTrue, reason: "backwards movement valid"); |
| 155 expect(it.current, equals(30)); |
| 156 }); |
| 157 |
| 158 test("before the set", () { |
| 159 var it = tree.fromIterator(0); |
| 160 expect(it.current, isNull); |
| 161 expect(it.movePrevious(), isFalse, reason: "not previous items"); |
| 162 expect(it.moveNext(), isTrue, reason: "forwards movement valid"); |
| 163 expect(it.current, equals(10)); |
| 164 }); |
| 165 |
| 166 test("inserted midpoint, non-inclusive, works forward", () { |
| 167 var it = tree.fromIterator(20, inclusive: false); |
| 168 expect(it.current, isNull, reason: "iteration starts with null"); |
| 169 expect(it.moveNext(), isTrue, reason: "moveNext() from spot works"); |
| 170 expect(it.current, equals(21)); |
| 171 }); |
| 172 |
| 173 test("inserted endpoint, non-inclusive, works forward", () { |
| 174 var it = tree.fromIterator(30, inclusive: false); |
| 175 expect(it.current, isNull, reason: "iteration starts with null"); |
| 176 expect(it.moveNext(), isFalse, reason: "moveNext() from spot works"); |
| 177 |
| 178 it = tree.fromIterator(10, inclusive: false); |
| 179 expect(it.current, isNull, reason: "iteration starts with null"); |
| 180 expect(it.moveNext(), isTrue, reason: "moveNext() from spot works"); |
| 181 expect(it.current, equals(15), |
| 182 reason: "non-inclusive start should be 15"); |
| 183 }); |
| 184 |
| 185 test("inserted endpoint, non-inclusive, works backward", () { |
| 186 var it = tree.fromIterator(10, inclusive: false); |
| 187 expect(it.current, isNull, reason: "iteration starts with null"); |
| 188 expect(it.movePrevious(), isFalse, |
| 189 reason: "movePrevious() from spot is null"); |
| 190 |
| 191 it = tree.fromIterator(30, inclusive: false); |
| 192 expect(it.current, isNull, reason: "iteration starts with null"); |
| 193 expect( |
| 194 it.movePrevious(), isTrue, reason: "moveNext() from spot works"); |
| 195 expect(it.current, equals(21)); |
| 196 }); |
| 197 |
| 198 test("inserted midpoint, non-inclusive, reversed, works forward", () { |
| 199 var it = tree.fromIterator(20, inclusive: false, reversed: true); |
| 200 expect(it.current, isNull, reason: "iteration starts with null"); |
| 201 expect(it.moveNext(), isTrue, reason: "moveNext() from spot works"); |
| 202 expect(it.current, equals(15)); |
| 203 }); |
| 204 |
| 205 test("inserted endpoint, non-inclusive, reversed, works forward", () { |
| 206 var it = tree.fromIterator(30, inclusive: false, reversed: true); |
| 207 expect(it.current, isNull, reason: "iteration starts with null"); |
| 208 expect(it.moveNext(), isTrue, reason: "moveNext() from spot works"); |
| 209 expect(it.current, equals(21)); |
| 210 |
| 211 it = tree.fromIterator(10, inclusive: false, reversed: true); |
| 212 expect(it.current, isNull, reason: "iteration starts with null"); |
| 213 expect(it.moveNext(), isFalse, reason: "moveNext() works"); |
| 214 }); |
| 215 |
| 216 test("inserted endpoint, non-inclusive, reversed, works backward", () { |
| 217 var it = tree.fromIterator(10, inclusive: false, reversed: true); |
| 218 expect(it.current, isNull, reason: "iteration starts with null"); |
| 219 expect( |
| 220 it.movePrevious(), isTrue, reason: "moveNext() from spot works"); |
| 221 expect(it.current, equals(15)); |
| 222 |
| 223 it = tree.fromIterator(30, inclusive: false, reversed: true); |
| 224 expect(it.current, isNull, reason: "iteration starts with null"); |
| 225 expect( |
| 226 it.movePrevious(), isFalse, reason: "moveNext() from spot works"); |
| 227 }); |
| 228 }); |
| 229 |
| 230 group("fails", () { |
| 231 var it; |
| 232 setUp(() => it = tree.iterator); |
| 233 |
| 234 test("after tree is cleared", () { |
| 235 tree.clear(); |
| 236 var error; |
| 237 try { |
| 238 it.moveNext(); |
| 239 } catch (e) { |
| 240 error = e; |
| 241 } |
| 242 expect(error, isConcurrentModificationError); |
| 243 }); |
| 244 |
| 245 test("after inserting an element", () { |
| 246 tree.add(101); |
| 247 var error; |
| 248 try { |
| 249 it.moveNext(); |
| 250 } catch (e) { |
| 251 error = e; |
| 252 } |
| 253 expect(error, isConcurrentModificationError); |
| 254 }); |
| 255 |
| 256 test("after removing an element", () { |
| 257 tree.remove(10); |
| 258 var error; |
| 259 try { |
| 260 it.moveNext(); |
| 261 } catch (e) { |
| 262 error = e; |
| 263 } |
| 264 expect(error, isConcurrentModificationError); |
| 265 }); |
| 266 }); |
| 267 |
| 268 group("still works", () { |
| 269 var it; |
| 270 setUp(() => it = tree.iterator); |
| 271 |
| 272 test("when removing non-existing element", () { |
| 273 tree.remove(42); |
| 274 var error; |
| 275 try { |
| 276 it.moveNext(); |
| 277 } catch (e) { |
| 278 error = e; |
| 279 } |
| 280 expect(error, isNull, reason: "set was not modified"); |
| 281 }); |
| 282 test("when adding an already existing element", () { |
| 283 tree.add(10); |
| 284 var error; |
| 285 try { |
| 286 it.moveNext(); |
| 287 } catch (e) { |
| 288 error = e; |
| 289 } |
| 290 expect(error, isNull, reason: "set was not modified"); |
| 291 }); |
| 292 }); |
| 293 }); |
| 294 |
| 295 group("set math", () { |
| 296 /// NOTE: set math with sorted sets should have a performance benifit, |
| 297 /// we do not check the performance, only that the resulting math |
| 298 /// is equivilant to non-sorted sets. |
| 299 |
| 300 TreeSet<num> tree; |
| 301 List<num> expectedUnion; |
| 302 List<num> expectedIntersection; |
| 303 List<num> expectedDifference; |
| 304 Set<num> nonSortedTestSet; |
| 305 TreeSet<num> sortedTestSet; |
| 306 |
| 307 setUp(() { |
| 308 tree = new TreeSet()..addAll([10, 20, 15, 21, 30, 20]); |
| 309 expectedUnion = [10, 15, 18, 20, 21, 22, 30]; |
| 310 expectedIntersection = [10, 15]; |
| 311 expectedDifference = [20, 21, 30]; |
| 312 nonSortedTestSet = new Set.from([10, 18, 22, 15]); |
| 313 sortedTestSet = new TreeSet()..addAll(nonSortedTestSet); |
| 314 }); |
| 315 |
| 316 test("union with non sorted set", () => |
| 317 expect(tree.union(nonSortedTestSet).toList(), equals(expectedUnion))); |
| 318 test("union with sorted set", () => |
| 319 expect(tree.union(sortedTestSet).toList(), equals(expectedUnion))); |
| 320 test("intersection with non sorted set", () => expect( |
| 321 tree.intersection(nonSortedTestSet).toList(), |
| 322 equals(expectedIntersection))); |
| 323 test("intersection with sorted set", () => expect( |
| 324 tree.intersection(sortedTestSet).toList(), |
| 325 equals(expectedIntersection))); |
| 326 test("difference with non sorted set", () => expect( |
| 327 tree.difference(nonSortedTestSet).toList(), |
| 328 equals(expectedDifference))); |
| 329 test("difference with sorted set", () => expect( |
| 330 tree.difference(sortedTestSet).toList(), equals(expectedDifference))); |
| 331 }); |
| 332 |
| 333 group("AVL implementaiton", () { |
| 334 /// NOTE: This is implementation specific testing for coverage. |
| 335 /// Users do not have access to [AvlNode] or [AvlTreeSet] |
| 336 test("RightLeftRotation", () { |
| 337 AvlTreeSet<num> tree = new TreeSet<num>(); |
| 338 tree.add(10); |
| 339 tree.add(20); |
| 340 tree.add(15); |
| 341 |
| 342 AvlNode ten = tree.getNode(10); |
| 343 AvlNode twenty = tree.getNode(20); |
| 344 AvlNode fifteen = tree.getNode(15); |
| 345 |
| 346 expect(ten.parent, equals(fifteen)); |
| 347 expect(ten.left, equals(null)); |
| 348 expect(ten.right, equals(null)); |
| 349 expect(ten.balance, equals(0)); |
| 350 |
| 351 expect(twenty.parent, equals(fifteen)); |
| 352 expect(twenty.left, equals(null)); |
| 353 expect(twenty.right, equals(null)); |
| 354 expect(twenty.balance, equals(0)); |
| 355 |
| 356 expect(fifteen.parent, equals(null)); |
| 357 expect(fifteen.left, equals(ten)); |
| 358 expect(fifteen.right, equals(twenty)); |
| 359 expect(fifteen.balance, equals(0)); |
| 360 }); |
| 361 test("LeftRightRotation", () { |
| 362 AvlTreeSet<num> tree = new TreeSet<num>(); |
| 363 tree.add(30); |
| 364 tree.add(10); |
| 365 tree.add(20); |
| 366 |
| 367 AvlNode thirty = tree.getNode(30); |
| 368 AvlNode ten = tree.getNode(10); |
| 369 AvlNode twenty = tree.getNode(20); |
| 370 |
| 371 expect(thirty.parent, equals(twenty)); |
| 372 expect(thirty.left, equals(null)); |
| 373 expect(thirty.right, equals(null)); |
| 374 expect(thirty.balance, equals(0)); |
| 375 |
| 376 expect(ten.parent, equals(twenty)); |
| 377 expect(ten.left, equals(null)); |
| 378 expect(ten.right, equals(null)); |
| 379 expect(ten.balance, equals(0)); |
| 380 |
| 381 expect(twenty.parent, equals(null)); |
| 382 expect(twenty.left, equals(ten)); |
| 383 expect(twenty.right, equals(thirty)); |
| 384 expect(twenty.balance, equals(0)); |
| 385 }); |
| 386 |
| 387 test("AVL-LeftRotation", () { |
| 388 AvlTreeSet<num> tree = new TreeSet<num>(); |
| 389 tree.add(1); |
| 390 tree.add(2); |
| 391 tree.add(3); |
| 392 |
| 393 AvlNode one = tree.getNode(1); |
| 394 AvlNode two = tree.getNode(2); |
| 395 AvlNode three = tree.getNode(3); |
| 396 |
| 397 expect(one.parent, equals(two)); |
| 398 expect(one.left, equals(null)); |
| 399 expect(one.right, equals(null)); |
| 400 expect(one.balance, equals(0)); |
| 401 |
| 402 expect(three.parent, equals(two)); |
| 403 expect(three.left, equals(null)); |
| 404 expect(three.right, equals(null)); |
| 405 expect(three.balance, equals(0)); |
| 406 |
| 407 expect(two.parent, equals(null)); |
| 408 expect(two.left, equals(one)); |
| 409 expect(two.right, equals(three)); |
| 410 expect(two.balance, equals(0)); |
| 411 }); |
| 412 |
| 413 test("AVL-RightRotation", () { |
| 414 AvlTreeSet<num> tree = new TreeSet<num>(); |
| 415 tree.add(3); |
| 416 tree.add(2); |
| 417 tree.add(1); |
| 418 |
| 419 AvlNode one = tree.getNode(1); |
| 420 AvlNode two = tree.getNode(2); |
| 421 AvlNode three = tree.getNode(3); |
| 422 |
| 423 expect(one.parent, equals(two)); |
| 424 expect(one.left, equals(null)); |
| 425 expect(one.right, equals(null)); |
| 426 expect(one.balance, equals(0)); |
| 427 |
| 428 expect(three.parent, equals(two)); |
| 429 expect(three.left, equals(null)); |
| 430 expect(three.right, equals(null)); |
| 431 expect(three.balance, equals(0)); |
| 432 |
| 433 expect(two.parent, equals(null)); |
| 434 expect(two.left, equals(one)); |
| 435 expect(two.right, equals(three)); |
| 436 expect(two.balance, equals(0)); |
| 437 }); |
| 438 }); |
| 439 |
| 440 group("nearest search", () { |
| 441 TreeSet<num> tree; |
| 442 setUp(() { |
| 443 tree = new TreeSet<num>(comparator: (num left, num right) { |
| 444 return left - right; |
| 445 })..addAll([300, 200, 100]); |
| 446 }); |
| 447 |
| 448 test("NEAREST is sane", () { |
| 449 var val = tree.nearest(199); |
| 450 expect(val, equals(200), reason: "199 is closer to 200"); |
| 451 val = tree.nearest(201); |
| 452 expect(val, equals(200), reason: "201 is 200"); |
| 453 val = tree.nearest(150); |
| 454 expect(val, equals(100), reason: "150 defaults to lower 100"); |
| 455 }); |
| 456 |
| 457 test("LESS_THAN is sane", () { |
| 458 var val = tree.nearest(199, nearestOption: TreeSearch.LESS_THAN); |
| 459 expect(val, equals(100), reason: "199 rounds down to 100"); |
| 460 }); |
| 461 |
| 462 test("GREATER_THAN is sane", () { |
| 463 var val = tree.nearest(101, nearestOption: TreeSearch.GREATER_THAN); |
| 464 expect(val, equals(200), reason: "101 rounds up to 200"); |
| 465 }); |
| 466 }); |
| 467 }); |
| 468 } |
OLD | NEW |