1 /* 2 * Copyright (C) 2008 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.common.collect; 18 19 import static com.google.common.base.Objects.equal; 20 import static com.google.common.collect.Platform.reduceExponentIfGwt; 21 import static com.google.common.collect.Platform.reduceIterationsIfGwt; 22 import static com.google.common.truth.Truth.assertThat; 23 24 import com.google.common.annotations.GwtCompatible; 25 import com.google.common.annotations.GwtIncompatible; 26 import com.google.common.collect.testing.IteratorFeature; 27 import com.google.common.collect.testing.IteratorTester; 28 import com.google.common.collect.testing.QueueTestSuiteBuilder; 29 import com.google.common.collect.testing.TestStringQueueGenerator; 30 import com.google.common.collect.testing.features.CollectionFeature; 31 import com.google.common.collect.testing.features.CollectionSize; 32 import com.google.common.testing.NullPointerTester; 33 import java.util.ArrayList; 34 import java.util.Arrays; 35 import java.util.Collection; 36 import java.util.Collections; 37 import java.util.ConcurrentModificationException; 38 import java.util.Iterator; 39 import java.util.List; 40 import java.util.Map; 41 import java.util.NoSuchElementException; 42 import java.util.PriorityQueue; 43 import java.util.Queue; 44 import java.util.Random; 45 import java.util.SortedMap; 46 import java.util.concurrent.atomic.AtomicInteger; 47 import junit.framework.Test; 48 import junit.framework.TestCase; 49 import junit.framework.TestSuite; 50 51 /** 52 * Unit test for {@link MinMaxPriorityQueue}. 53 * 54 * @author Alexei Stolboushkin 55 * @author Sverre Sundsdal 56 */ 57 @GwtCompatible(emulated = true) 58 public class MinMaxPriorityQueueTest extends TestCase { 59 private static final Ordering<Integer> SOME_COMPARATOR = Ordering.natural().reverse(); 60 61 @GwtIncompatible // suite suite()62 public static Test suite() { 63 TestSuite suite = new TestSuite(); 64 suite.addTestSuite(MinMaxPriorityQueueTest.class); 65 suite.addTest( 66 QueueTestSuiteBuilder.using( 67 new TestStringQueueGenerator() { 68 @Override 69 protected Queue<String> create(String[] elements) { 70 return MinMaxPriorityQueue.create(Arrays.asList(elements)); 71 } 72 }) 73 .named("MinMaxPriorityQueue") 74 .withFeatures(CollectionSize.ANY, CollectionFeature.GENERAL_PURPOSE) 75 .createTestSuite()); 76 return suite; 77 } 78 79 // Overkill alert! Test all combinations of 0-2 options during creation. 80 testCreation_simple()81 public void testCreation_simple() { 82 MinMaxPriorityQueue<Integer> queue = MinMaxPriorityQueue.create(); 83 assertEquals(11, queue.capacity()); 84 checkUnbounded(queue); 85 checkNatural(queue); 86 } 87 testCreation_comparator()88 public void testCreation_comparator() { 89 MinMaxPriorityQueue<Integer> queue = MinMaxPriorityQueue.orderedBy(SOME_COMPARATOR).create(); 90 assertEquals(11, queue.capacity()); 91 checkUnbounded(queue); 92 assertSame(SOME_COMPARATOR, queue.comparator()); 93 } 94 testCreation_expectedSize()95 public void testCreation_expectedSize() { 96 MinMaxPriorityQueue<Integer> queue = MinMaxPriorityQueue.expectedSize(8).create(); 97 assertEquals(8, queue.capacity()); 98 checkUnbounded(queue); 99 checkNatural(queue); 100 } 101 testCreation_expectedSize_comparator()102 public void testCreation_expectedSize_comparator() { 103 MinMaxPriorityQueue<Integer> queue = 104 MinMaxPriorityQueue.orderedBy(SOME_COMPARATOR).expectedSize(8).create(); 105 assertEquals(8, queue.capacity()); 106 checkUnbounded(queue); 107 assertSame(SOME_COMPARATOR, queue.comparator()); 108 } 109 testCreation_maximumSize()110 public void testCreation_maximumSize() { 111 MinMaxPriorityQueue<Integer> queue = MinMaxPriorityQueue.maximumSize(42).create(); 112 assertEquals(11, queue.capacity()); 113 assertEquals(42, queue.maximumSize); 114 checkNatural(queue); 115 } 116 testCreation_comparator_maximumSize()117 public void testCreation_comparator_maximumSize() { 118 MinMaxPriorityQueue<Integer> queue = 119 MinMaxPriorityQueue.orderedBy(SOME_COMPARATOR).maximumSize(42).create(); 120 assertEquals(11, queue.capacity()); 121 assertEquals(42, queue.maximumSize); 122 assertSame(SOME_COMPARATOR, queue.comparator()); 123 } 124 testCreation_expectedSize_maximumSize()125 public void testCreation_expectedSize_maximumSize() { 126 MinMaxPriorityQueue<Integer> queue = 127 MinMaxPriorityQueue.expectedSize(8).maximumSize(42).create(); 128 assertEquals(8, queue.capacity()); 129 assertEquals(42, queue.maximumSize); 130 checkNatural(queue); 131 } 132 133 private static final ImmutableList<Integer> NUMBERS = ImmutableList.of(4, 8, 15, 16, 23, 42); 134 testCreation_withContents()135 public void testCreation_withContents() { 136 MinMaxPriorityQueue<Integer> queue = MinMaxPriorityQueue.create(NUMBERS); 137 assertEquals(6, queue.size()); 138 assertEquals(11, queue.capacity()); 139 checkUnbounded(queue); 140 checkNatural(queue); 141 } 142 testCreation_comparator_withContents()143 public void testCreation_comparator_withContents() { 144 MinMaxPriorityQueue<Integer> queue = 145 MinMaxPriorityQueue.orderedBy(SOME_COMPARATOR).create(NUMBERS); 146 assertEquals(6, queue.size()); 147 assertEquals(11, queue.capacity()); 148 checkUnbounded(queue); 149 assertSame(SOME_COMPARATOR, queue.comparator()); 150 } 151 testCreation_expectedSize_withContents()152 public void testCreation_expectedSize_withContents() { 153 MinMaxPriorityQueue<Integer> queue = MinMaxPriorityQueue.expectedSize(8).create(NUMBERS); 154 assertEquals(6, queue.size()); 155 assertEquals(8, queue.capacity()); 156 checkUnbounded(queue); 157 checkNatural(queue); 158 } 159 testCreation_maximumSize_withContents()160 public void testCreation_maximumSize_withContents() { 161 MinMaxPriorityQueue<Integer> queue = MinMaxPriorityQueue.maximumSize(42).create(NUMBERS); 162 assertEquals(6, queue.size()); 163 assertEquals(11, queue.capacity()); 164 assertEquals(42, queue.maximumSize); 165 checkNatural(queue); 166 } 167 168 // Now test everything at once 169 testCreation_allOptions()170 public void testCreation_allOptions() { 171 MinMaxPriorityQueue<Integer> queue = 172 MinMaxPriorityQueue.orderedBy(SOME_COMPARATOR) 173 .expectedSize(8) 174 .maximumSize(42) 175 .create(NUMBERS); 176 assertEquals(6, queue.size()); 177 assertEquals(8, queue.capacity()); 178 assertEquals(42, queue.maximumSize); 179 assertSame(SOME_COMPARATOR, queue.comparator()); 180 } 181 182 // TODO: tests that check the weird interplay between expected size, 183 // maximum size, size of initial contents, default capacity... 184 checkNatural(MinMaxPriorityQueue<Integer> queue)185 private static void checkNatural(MinMaxPriorityQueue<Integer> queue) { 186 assertSame(Ordering.natural(), queue.comparator()); 187 } 188 checkUnbounded(MinMaxPriorityQueue<Integer> queue)189 private static void checkUnbounded(MinMaxPriorityQueue<Integer> queue) { 190 assertEquals(Integer.MAX_VALUE, queue.maximumSize); 191 } 192 testHeapIntact()193 public void testHeapIntact() { 194 Random random = new Random(0); 195 int heapSize = 99; 196 int numberOfModifications = 100; 197 MinMaxPriorityQueue<Integer> mmHeap = MinMaxPriorityQueue.expectedSize(heapSize).create(); 198 /* 199 * this map would contain the same exact elements as the MinMaxHeap; the 200 * value in the map is the number of occurrences of the key. 201 */ 202 SortedMap<Integer, AtomicInteger> replica = Maps.newTreeMap(); 203 assertTrue("Empty heap should be OK", mmHeap.isIntact()); 204 for (int i = 0; i < heapSize; i++) { 205 int randomInt = random.nextInt(); 206 mmHeap.offer(randomInt); 207 insertIntoReplica(replica, randomInt); 208 } 209 assertIntact(mmHeap); 210 assertEquals(heapSize, mmHeap.size()); 211 int currentHeapSize = heapSize; 212 for (int i = 0; i < numberOfModifications; i++) { 213 if (random.nextBoolean()) { 214 /* insert a new element */ 215 int randomInt = random.nextInt(); 216 mmHeap.offer(randomInt); 217 insertIntoReplica(replica, randomInt); 218 currentHeapSize++; 219 } else { 220 /* remove either min or max */ 221 if (random.nextBoolean()) { 222 removeMinFromReplica(replica, mmHeap.poll()); 223 } else { 224 removeMaxFromReplica(replica, mmHeap.pollLast()); 225 } 226 for (Integer v : replica.keySet()) { 227 assertThat(mmHeap).contains(v); 228 } 229 assertIntact(mmHeap); 230 currentHeapSize--; 231 assertEquals(currentHeapSize, mmHeap.size()); 232 } 233 } 234 assertEquals(currentHeapSize, mmHeap.size()); 235 assertIntact(mmHeap); 236 } 237 testSmall()238 public void testSmall() { 239 MinMaxPriorityQueue<Integer> mmHeap = MinMaxPriorityQueue.create(); 240 mmHeap.add(1); 241 mmHeap.add(4); 242 mmHeap.add(2); 243 mmHeap.add(3); 244 assertEquals(4, (int) mmHeap.pollLast()); 245 assertEquals(3, (int) mmHeap.peekLast()); 246 assertEquals(3, (int) mmHeap.pollLast()); 247 assertEquals(1, (int) mmHeap.peek()); 248 assertEquals(2, (int) mmHeap.peekLast()); 249 assertEquals(2, (int) mmHeap.pollLast()); 250 assertEquals(1, (int) mmHeap.peek()); 251 assertEquals(1, (int) mmHeap.peekLast()); 252 assertEquals(1, (int) mmHeap.pollLast()); 253 assertNull(mmHeap.peek()); 254 assertNull(mmHeap.peekLast()); 255 assertNull(mmHeap.pollLast()); 256 } 257 testSmallMinHeap()258 public void testSmallMinHeap() { 259 MinMaxPriorityQueue<Integer> mmHeap = MinMaxPriorityQueue.create(); 260 mmHeap.add(1); 261 mmHeap.add(3); 262 mmHeap.add(2); 263 assertEquals(1, (int) mmHeap.peek()); 264 assertEquals(1, (int) mmHeap.poll()); 265 assertEquals(3, (int) mmHeap.peekLast()); 266 assertEquals(2, (int) mmHeap.peek()); 267 assertEquals(2, (int) mmHeap.poll()); 268 assertEquals(3, (int) mmHeap.peekLast()); 269 assertEquals(3, (int) mmHeap.peek()); 270 assertEquals(3, (int) mmHeap.poll()); 271 assertNull(mmHeap.peekLast()); 272 assertNull(mmHeap.peek()); 273 assertNull(mmHeap.poll()); 274 } 275 testRemove()276 public void testRemove() { 277 MinMaxPriorityQueue<Integer> mmHeap = MinMaxPriorityQueue.create(); 278 mmHeap.addAll(Lists.newArrayList(1, 2, 3, 4, 47, 1, 5, 3, 0)); 279 assertTrue("Heap is not intact initally", mmHeap.isIntact()); 280 assertEquals(9, mmHeap.size()); 281 mmHeap.remove(5); 282 assertEquals(8, mmHeap.size()); 283 assertTrue("Heap is not intact after remove()", mmHeap.isIntact()); 284 assertEquals(47, (int) mmHeap.pollLast()); 285 assertEquals(4, (int) mmHeap.pollLast()); 286 mmHeap.removeAll(Lists.newArrayList(2, 3)); 287 assertEquals(3, mmHeap.size()); 288 assertTrue("Heap is not intact after removeAll()", mmHeap.isIntact()); 289 } 290 testContains()291 public void testContains() { 292 MinMaxPriorityQueue<Integer> mmHeap = MinMaxPriorityQueue.create(); 293 mmHeap.addAll(Lists.newArrayList(1, 1, 2)); 294 assertEquals(3, mmHeap.size()); 295 assertFalse("Heap does not contain null", mmHeap.contains(null)); 296 assertFalse("Heap does not contain 3", mmHeap.contains(3)); 297 assertFalse("Heap does not contain 3", mmHeap.remove(3)); 298 assertEquals(3, mmHeap.size()); 299 assertTrue("Heap is not intact after remove()", mmHeap.isIntact()); 300 assertTrue("Heap contains two 1's", mmHeap.contains(1)); 301 assertTrue("Heap contains two 1's", mmHeap.remove(1)); 302 assertTrue("Heap contains 1", mmHeap.contains(1)); 303 assertTrue("Heap contains 1", mmHeap.remove(1)); 304 assertFalse("Heap does not contain 1", mmHeap.contains(1)); 305 assertTrue("Heap contains 2", mmHeap.remove(2)); 306 assertEquals(0, mmHeap.size()); 307 assertFalse("Heap does not contain anything", mmHeap.contains(1)); 308 assertFalse("Heap does not contain anything", mmHeap.remove(2)); 309 } 310 testIteratorPastEndException()311 public void testIteratorPastEndException() { 312 MinMaxPriorityQueue<Integer> mmHeap = MinMaxPriorityQueue.create(); 313 mmHeap.addAll(Lists.newArrayList(1, 2)); 314 Iterator<Integer> it = mmHeap.iterator(); 315 assertTrue("Iterator has reached end prematurely", it.hasNext()); 316 it.next(); 317 it.next(); 318 try { 319 it.next(); 320 fail("No exception thrown when iterating past end of heap"); 321 } catch (NoSuchElementException expected) { 322 } 323 } 324 testIteratorConcurrentModification()325 public void testIteratorConcurrentModification() { 326 MinMaxPriorityQueue<Integer> mmHeap = MinMaxPriorityQueue.create(); 327 mmHeap.addAll(Lists.newArrayList(1, 2, 3, 4)); 328 Iterator<Integer> it = mmHeap.iterator(); 329 assertTrue("Iterator has reached end prematurely", it.hasNext()); 330 it.next(); 331 it.next(); 332 mmHeap.remove(4); 333 try { 334 it.next(); 335 fail("No exception thrown when iterating a modified heap"); 336 } catch (ConcurrentModificationException expected) { 337 } 338 } 339 340 /** Tests a failure caused by fix to childless uncle issue. */ testIteratorRegressionChildlessUncle()341 public void testIteratorRegressionChildlessUncle() { 342 final ArrayList<Integer> initial = Lists.newArrayList(1, 15, 13, 8, 9, 10, 11, 14); 343 MinMaxPriorityQueue<Integer> q = MinMaxPriorityQueue.create(initial); 344 assertIntact(q); 345 q.remove(9); 346 q.remove(11); 347 q.remove(10); 348 // Now we're in the critical state: [1, 15, 13, 8, 14] 349 // Removing 8 while iterating caused duplicates in iteration result. 350 List<Integer> result = Lists.newArrayListWithCapacity(initial.size()); 351 for (Iterator<Integer> iter = q.iterator(); iter.hasNext(); ) { 352 Integer value = iter.next(); 353 result.add(value); 354 if (value == 8) { 355 iter.remove(); 356 } 357 } 358 assertIntact(q); 359 assertThat(result).containsExactly(1, 15, 13, 8, 14); 360 } 361 362 /** 363 * This tests a special case of the removeAt() call. Moving an element sideways on the heap could 364 * break the invariants. Sometimes we need to bubble an element up instead of trickling down. See 365 * implementation. 366 */ testInvalidatingRemove()367 public void testInvalidatingRemove() { 368 MinMaxPriorityQueue<Integer> mmHeap = MinMaxPriorityQueue.create(); 369 mmHeap.addAll( 370 Lists.newArrayList(1, 20, 1000, 2, 3, 30, 40, 10, 11, 12, 13, 300, 400, 500, 600)); 371 assertEquals(15, mmHeap.size()); 372 assertTrue("Heap is not intact initially", mmHeap.isIntact()); 373 mmHeap.remove(12); 374 assertEquals(14, mmHeap.size()); 375 assertTrue("Heap is not intact after remove()", mmHeap.isIntact()); 376 } 377 378 /** This tests a more obscure special case, but otherwise similar to above. */ testInvalidatingRemove2()379 public void testInvalidatingRemove2() { 380 MinMaxPriorityQueue<Integer> mmHeap = MinMaxPriorityQueue.create(); 381 List<Integer> values = 382 Lists.newArrayList( 383 1, 20, 1000, 2, 3, 30, 40, 10, 11, 12, 13, 300, 400, 500, 600, 4, 5, 6, 7, 8, 9, 4, 5, 384 200, 250); 385 mmHeap.addAll(values); 386 assertEquals(25, mmHeap.size()); 387 assertTrue("Heap is not intact initially", mmHeap.isIntact()); 388 mmHeap.remove(2); 389 assertEquals(24, mmHeap.size()); 390 assertTrue("Heap is not intact after remove()", mmHeap.isIntact()); 391 values.removeAll(Lists.newArrayList(2)); 392 assertEquals(values.size(), mmHeap.size()); 393 assertTrue(values.containsAll(mmHeap)); 394 assertTrue(mmHeap.containsAll(values)); 395 } 396 testIteratorInvalidatingIteratorRemove()397 public void testIteratorInvalidatingIteratorRemove() { 398 MinMaxPriorityQueue<Integer> mmHeap = MinMaxPriorityQueue.create(); 399 mmHeap.addAll(Lists.newArrayList(1, 20, 100, 2, 3, 30, 40)); 400 assertEquals(7, mmHeap.size()); 401 assertTrue("Heap is not intact initially", mmHeap.isIntact()); 402 Iterator<Integer> it = mmHeap.iterator(); 403 assertEquals((Integer) 1, it.next()); 404 assertEquals((Integer) 20, it.next()); 405 assertEquals((Integer) 100, it.next()); 406 assertEquals((Integer) 2, it.next()); 407 it.remove(); 408 assertFalse(mmHeap.contains(2)); 409 assertTrue(it.hasNext()); 410 assertEquals((Integer) 3, it.next()); 411 assertTrue(it.hasNext()); 412 assertEquals((Integer) 30, it.next()); 413 assertTrue(it.hasNext()); 414 assertEquals((Integer) 40, it.next()); 415 assertFalse(it.hasNext()); 416 assertEquals(6, mmHeap.size()); 417 assertTrue("Heap is not intact after remove()", mmHeap.isIntact()); 418 assertFalse(mmHeap.contains(2)); 419 420 // This tests that it.remove() above actually changed the order. It 421 // indicates that the value 40 was stored in forgetMeNot, so it is 422 // returned in the last call to it.next(). Without it, 30 should be the last 423 // item returned by the iterator. 424 Integer lastItem = 0; 425 for (Integer tmp : mmHeap) { 426 lastItem = tmp; 427 } 428 assertEquals((Integer) 30, lastItem); 429 } 430 431 /** 432 * This tests a special case where removeAt has to trickle an element first down one level from a 433 * min to a max level, then up one level above the index of the removed element. It also tests 434 * that skipMe in the iterator plays nicely with forgetMeNot. 435 */ testIteratorInvalidatingIteratorRemove2()436 public void testIteratorInvalidatingIteratorRemove2() { 437 MinMaxPriorityQueue<Integer> mmHeap = MinMaxPriorityQueue.create(); 438 mmHeap.addAll( 439 Lists.newArrayList(1, 20, 1000, 2, 3, 30, 40, 10, 11, 12, 13, 200, 300, 500, 400)); 440 assertTrue("Heap is not intact initially", mmHeap.isIntact()); 441 Iterator<Integer> it = mmHeap.iterator(); 442 assertEquals((Integer) 1, it.next()); 443 assertEquals((Integer) 20, it.next()); 444 assertEquals((Integer) 1000, it.next()); 445 assertEquals((Integer) 2, it.next()); 446 it.remove(); 447 // After this remove, 400 has moved up and 20 down past cursor 448 assertTrue("Heap is not intact after remove", mmHeap.isIntact()); 449 assertEquals((Integer) 10, it.next()); 450 assertEquals((Integer) 3, it.next()); 451 it.remove(); 452 // After this remove, 400 moved down again and 500 up past the cursor 453 assertTrue("Heap is not intact after remove", mmHeap.isIntact()); 454 assertEquals((Integer) 12, it.next()); 455 assertEquals((Integer) 30, it.next()); 456 assertEquals((Integer) 40, it.next()); 457 // Skipping 20 458 assertEquals((Integer) 11, it.next()); 459 // Not skipping 400, because it moved back down 460 assertEquals((Integer) 400, it.next()); 461 assertEquals((Integer) 13, it.next()); 462 assertEquals((Integer) 200, it.next()); 463 assertEquals((Integer) 300, it.next()); 464 // Last from forgetMeNot. 465 assertEquals((Integer) 500, it.next()); 466 } 467 testRemoveFromStringHeap()468 public void testRemoveFromStringHeap() { 469 MinMaxPriorityQueue<String> mmHeap = MinMaxPriorityQueue.expectedSize(5).create(); 470 Collections.addAll(mmHeap, "foo", "bar", "foobar", "barfoo", "larry", "sergey", "eric"); 471 assertTrue("Heap is not intact initially", mmHeap.isIntact()); 472 assertEquals("bar", mmHeap.peek()); 473 assertEquals("sergey", mmHeap.peekLast()); 474 assertEquals(7, mmHeap.size()); 475 assertTrue("Could not remove larry", mmHeap.remove("larry")); 476 assertEquals(6, mmHeap.size()); 477 assertFalse("heap contains larry which has been removed", mmHeap.contains("larry")); 478 assertTrue("heap does not contain sergey", mmHeap.contains("sergey")); 479 assertTrue("Could not remove larry", mmHeap.removeAll(Lists.newArrayList("sergey", "eric"))); 480 assertFalse("Could remove nikesh which is not in the heap", mmHeap.remove("nikesh")); 481 assertEquals(4, mmHeap.size()); 482 } 483 testCreateWithOrdering()484 public void testCreateWithOrdering() { 485 MinMaxPriorityQueue<String> mmHeap = 486 MinMaxPriorityQueue.orderedBy(Ordering.natural().reverse()).create(); 487 Collections.addAll(mmHeap, "foo", "bar", "foobar", "barfoo", "larry", "sergey", "eric"); 488 assertTrue("Heap is not intact initially", mmHeap.isIntact()); 489 assertEquals("sergey", mmHeap.peek()); 490 assertEquals("bar", mmHeap.peekLast()); 491 } 492 testCreateWithCapacityAndOrdering()493 public void testCreateWithCapacityAndOrdering() { 494 MinMaxPriorityQueue<Integer> mmHeap = 495 MinMaxPriorityQueue.orderedBy(Ordering.natural().reverse()).expectedSize(5).create(); 496 Collections.addAll(mmHeap, 1, 7, 2, 56, 2, 5, 23, 68, 0, 3); 497 assertTrue("Heap is not intact initially", mmHeap.isIntact()); 498 assertEquals(68, (int) mmHeap.peek()); 499 assertEquals(0, (int) mmHeap.peekLast()); 500 } 501 runIterator(final List<T> values, int steps)502 private <T extends Comparable<T>> void runIterator(final List<T> values, int steps) 503 throws Exception { 504 IteratorTester<T> tester = 505 new IteratorTester<T>( 506 steps, 507 IteratorFeature.MODIFIABLE, 508 Lists.newLinkedList(values), 509 IteratorTester.KnownOrder.UNKNOWN_ORDER) { 510 private MinMaxPriorityQueue<T> mmHeap; 511 512 @Override 513 protected Iterator<T> newTargetIterator() { 514 mmHeap = MinMaxPriorityQueue.create(values); 515 return mmHeap.iterator(); 516 } 517 518 @Override 519 protected void verify(List<T> elements) { 520 assertEquals(Sets.newHashSet(elements), Sets.newHashSet(mmHeap.iterator())); 521 assertIntact(mmHeap); 522 } 523 }; 524 tester.test(); 525 } 526 testIteratorTester()527 public void testIteratorTester() throws Exception { 528 Random random = new Random(0); 529 List<Integer> list = Lists.newArrayList(); 530 for (int i = 0; i < 3; i++) { 531 list.add(random.nextInt()); 532 } 533 runIterator(list, 6); 534 } 535 testIteratorTesterLarger()536 public void testIteratorTesterLarger() throws Exception { 537 runIterator(Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), 5); 538 } 539 testRemoveAt()540 public void testRemoveAt() { 541 long seed = new Random().nextLong(); 542 Random random = new Random(seed); 543 int heapSize = 999; 544 int numberOfModifications = reduceIterationsIfGwt(500); 545 MinMaxPriorityQueue<Integer> mmHeap = MinMaxPriorityQueue.expectedSize(heapSize).create(); 546 for (int i = 0; i < heapSize; i++) { 547 mmHeap.add(random.nextInt()); 548 } 549 for (int i = 0; i < numberOfModifications; i++) { 550 mmHeap.removeAt(random.nextInt(mmHeap.size())); 551 assertIntactUsingSeed(seed, mmHeap); 552 mmHeap.add(random.nextInt()); 553 assertIntactUsingSeed(seed, mmHeap); 554 } 555 } 556 testRemoveAt_exhaustive()557 public void testRemoveAt_exhaustive() { 558 int size = reduceExponentIfGwt(8); 559 List<Integer> expected = createOrderedList(size); 560 for (Collection<Integer> perm : Collections2.permutations(expected)) { 561 for (int i = 0; i < perm.size(); i++) { 562 MinMaxPriorityQueue<Integer> q = MinMaxPriorityQueue.create(perm); 563 q.removeAt(i); 564 assertIntactUsingStartedWith(perm, q); 565 } 566 } 567 } 568 569 /** Regression test for bug found. */ testCorrectOrdering_regression()570 public void testCorrectOrdering_regression() { 571 MinMaxPriorityQueue<Integer> q = MinMaxPriorityQueue.create(ImmutableList.of(3, 5, 1, 4, 7)); 572 List<Integer> expected = ImmutableList.of(1, 3, 4, 5, 7); 573 List<Integer> actual = new ArrayList<>(5); 574 for (int i = 0; i < expected.size(); i++) { 575 actual.add(q.pollFirst()); 576 } 577 assertEquals(expected, actual); 578 } 579 testCorrectOrdering_smallHeapsPollFirst()580 public void testCorrectOrdering_smallHeapsPollFirst() { 581 for (int size = 2; size < 16; size++) { 582 for (int attempts = 0; attempts < size * (size - 1); attempts++) { 583 ArrayList<Integer> elements = createOrderedList(size); 584 List<Integer> expected = ImmutableList.copyOf(elements); 585 MinMaxPriorityQueue<Integer> q = MinMaxPriorityQueue.create(); 586 long seed = insertRandomly(elements, q); 587 while (!q.isEmpty()) { 588 elements.add(q.pollFirst()); 589 } 590 assertEqualsUsingSeed(seed, expected, elements); 591 } 592 } 593 } 594 testCorrectOrdering_smallHeapsPollLast()595 public void testCorrectOrdering_smallHeapsPollLast() { 596 for (int size = 2; size < 16; size++) { 597 for (int attempts = 0; attempts < size * (size - 1); attempts++) { 598 ArrayList<Integer> elements = createOrderedList(size); 599 List<Integer> expected = ImmutableList.copyOf(elements); 600 MinMaxPriorityQueue<Integer> q = MinMaxPriorityQueue.create(); 601 long seed = insertRandomly(elements, q); 602 while (!q.isEmpty()) { 603 elements.add(0, q.pollLast()); 604 } 605 assertEqualsUsingSeed(seed, expected, elements); 606 } 607 } 608 } 609 testCorrectOrdering_mediumHeapsPollFirst()610 public void testCorrectOrdering_mediumHeapsPollFirst() { 611 for (int attempts = 0; attempts < reduceIterationsIfGwt(5000); attempts++) { 612 int size = new Random().nextInt(256) + 16; 613 ArrayList<Integer> elements = createOrderedList(size); 614 List<Integer> expected = ImmutableList.copyOf(elements); 615 MinMaxPriorityQueue<Integer> q = MinMaxPriorityQueue.create(); 616 long seed = insertRandomly(elements, q); 617 while (!q.isEmpty()) { 618 elements.add(q.pollFirst()); 619 } 620 assertEqualsUsingSeed(seed, expected, elements); 621 } 622 } 623 624 /** Regression test for bug found in random testing. */ testCorrectOrdering_73ElementBug()625 public void testCorrectOrdering_73ElementBug() { 626 int size = 73; 627 long seed = 7522346378524621981L; 628 ArrayList<Integer> elements = createOrderedList(size); 629 List<Integer> expected = ImmutableList.copyOf(elements); 630 MinMaxPriorityQueue<Integer> q = MinMaxPriorityQueue.create(); 631 insertRandomly(elements, q, new Random(seed)); 632 assertIntact(q); 633 while (!q.isEmpty()) { 634 elements.add(q.pollFirst()); 635 assertIntact(q); 636 } 637 assertEqualsUsingSeed(seed, expected, elements); 638 } 639 testCorrectOrdering_mediumHeapsPollLast()640 public void testCorrectOrdering_mediumHeapsPollLast() { 641 for (int attempts = 0; attempts < reduceIterationsIfGwt(5000); attempts++) { 642 int size = new Random().nextInt(256) + 16; 643 ArrayList<Integer> elements = createOrderedList(size); 644 List<Integer> expected = ImmutableList.copyOf(elements); 645 MinMaxPriorityQueue<Integer> q = MinMaxPriorityQueue.create(); 646 long seed = insertRandomly(elements, q); 647 while (!q.isEmpty()) { 648 elements.add(0, q.pollLast()); 649 } 650 assertEqualsUsingSeed(seed, expected, elements); 651 } 652 } 653 testCorrectOrdering_randomAccess()654 public void testCorrectOrdering_randomAccess() { 655 long seed = new Random().nextLong(); 656 Random random = new Random(seed); 657 PriorityQueue<Integer> control = new PriorityQueue<>(); 658 MinMaxPriorityQueue<Integer> q = MinMaxPriorityQueue.create(); 659 for (int i = 0; i < 73; i++) { // 73 is a childless uncle case. 660 Integer element = random.nextInt(); 661 control.add(element); 662 assertTrue(q.add(element)); 663 } 664 assertIntact(q); 665 for (int i = 0; i < reduceIterationsIfGwt(500_000); i++) { 666 if (random.nextBoolean()) { 667 Integer element = random.nextInt(); 668 control.add(element); 669 q.add(element); 670 } else { 671 assertEqualsUsingSeed(seed, control.poll(), q.pollFirst()); 672 } 673 } 674 while (!control.isEmpty()) { 675 assertEqualsUsingSeed(seed, control.poll(), q.pollFirst()); 676 } 677 assertTrue(q.isEmpty()); 678 } 679 testExhaustive_pollAndPush()680 public void testExhaustive_pollAndPush() { 681 int size = 5; 682 List<Integer> expected = createOrderedList(size); 683 for (Collection<Integer> perm : Collections2.permutations(expected)) { 684 MinMaxPriorityQueue<Integer> q = MinMaxPriorityQueue.create(perm); 685 List<Integer> elements = Lists.newArrayListWithCapacity(size); 686 while (!q.isEmpty()) { 687 Integer next = q.pollFirst(); 688 for (int i = 0; i <= size; i++) { 689 assertTrue(q.add(i)); 690 assertTrue(q.add(next)); 691 assertTrue(q.remove(i)); 692 assertEquals(next, q.poll()); 693 } 694 elements.add(next); 695 } 696 assertEqualsUsingStartedWith(perm, expected, elements); 697 } 698 } 699 700 /** Regression test for b/4124577 */ testRegression_dataCorruption()701 public void testRegression_dataCorruption() { 702 int size = 8; 703 List<Integer> expected = createOrderedList(size); 704 MinMaxPriorityQueue<Integer> q = MinMaxPriorityQueue.create(expected); 705 List<Integer> contents = Lists.newArrayList(expected); 706 List<Integer> elements = Lists.newArrayListWithCapacity(size); 707 while (!q.isEmpty()) { 708 assertThat(q).containsExactlyElementsIn(contents); 709 Integer next = q.pollFirst(); 710 contents.remove(next); 711 assertThat(q).containsExactlyElementsIn(contents); 712 for (int i = 0; i <= size; i++) { 713 q.add(i); 714 contents.add(i); 715 assertThat(q).containsExactlyElementsIn(contents); 716 q.add(next); 717 contents.add(next); 718 assertThat(q).containsExactlyElementsIn(contents); 719 q.remove(i); 720 assertTrue(contents.remove(Integer.valueOf(i))); 721 assertThat(q).containsExactlyElementsIn(contents); 722 assertEquals(next, q.poll()); 723 contents.remove(next); 724 assertThat(q).containsExactlyElementsIn(contents); 725 } 726 elements.add(next); 727 } 728 assertEquals(expected, elements); 729 } 730 731 /** Regression test for https://github.com/google/guava/issues/2658 */ testRemoveRegression()732 public void testRemoveRegression() { 733 MinMaxPriorityQueue<Long> queue = 734 MinMaxPriorityQueue.create(ImmutableList.of(2L, 3L, 0L, 4L, 1L)); 735 queue.remove(4L); 736 queue.remove(1L); 737 assertThat(queue).doesNotContain(1L); 738 } 739 testRandomRemoves()740 public void testRandomRemoves() { 741 Random random = new Random(0); 742 for (int attempts = 0; attempts < reduceIterationsIfGwt(1000); attempts++) { 743 ArrayList<Integer> elements = createOrderedList(10); 744 Collections.shuffle(elements, random); 745 MinMaxPriorityQueue<Integer> queue = MinMaxPriorityQueue.create(elements); 746 Collections.shuffle(elements, random); 747 for (Integer element : elements) { 748 assertThat(queue.remove(element)).isTrue(); 749 assertIntact(queue); 750 assertThat(queue).doesNotContain(element); 751 } 752 assertThat(queue).isEmpty(); 753 } 754 } 755 testRandomAddsAndRemoves()756 public void testRandomAddsAndRemoves() { 757 Random random = new Random(0); 758 Multiset<Integer> elements = HashMultiset.create(); 759 MinMaxPriorityQueue<Integer> queue = MinMaxPriorityQueue.create(); 760 int range = 10_000; // range should be small enough that equal elements occur semi-frequently 761 for (int iter = 0; iter < reduceIterationsIfGwt(1000); iter++) { 762 for (int i = 0; i < 100; i++) { 763 Integer element = random.nextInt(range); 764 elements.add(element); 765 queue.add(element); 766 } 767 Iterator<Integer> queueIterator = queue.iterator(); 768 int remaining = queue.size(); 769 while (queueIterator.hasNext()) { 770 Integer element = queueIterator.next(); 771 remaining--; 772 assertThat(elements).contains(element); 773 if (random.nextBoolean()) { 774 elements.remove(element); 775 queueIterator.remove(); 776 } 777 } 778 assertThat(remaining).isEqualTo(0); 779 assertIntact(queue); 780 assertThat(queue).containsExactlyElementsIn(elements); 781 } 782 } 783 784 private enum Element { 785 ONE, 786 TWO, 787 THREE, 788 FOUR, 789 FIVE; 790 } 791 testRandomAddsAndRemoves_duplicateElements()792 public void testRandomAddsAndRemoves_duplicateElements() { 793 Random random = new Random(0); 794 Multiset<Element> elements = HashMultiset.create(); 795 MinMaxPriorityQueue<Element> queue = MinMaxPriorityQueue.create(); 796 int range = Element.values().length; 797 for (int iter = 0; iter < reduceIterationsIfGwt(1000); iter++) { 798 for (int i = 0; i < 100; i++) { 799 Element element = Element.values()[random.nextInt(range)]; 800 elements.add(element); 801 queue.add(element); 802 } 803 Iterator<Element> queueIterator = queue.iterator(); 804 int remaining = queue.size(); 805 while (queueIterator.hasNext()) { 806 Element element = queueIterator.next(); 807 remaining--; 808 assertThat(elements).contains(element); 809 if (random.nextBoolean()) { 810 elements.remove(element); 811 queueIterator.remove(); 812 } 813 } 814 assertThat(remaining).isEqualTo(0); 815 assertIntact(queue); 816 assertThat(queue).containsExactlyElementsIn(elements); 817 } 818 } 819 820 /** Returns the seed used for the randomization. */ insertRandomly(ArrayList<Integer> elements, MinMaxPriorityQueue<Integer> q)821 private long insertRandomly(ArrayList<Integer> elements, MinMaxPriorityQueue<Integer> q) { 822 long seed = new Random().nextLong(); 823 Random random = new Random(seed); 824 insertRandomly(elements, q, random); 825 return seed; 826 } 827 insertRandomly( ArrayList<Integer> elements, MinMaxPriorityQueue<Integer> q, Random random)828 private static void insertRandomly( 829 ArrayList<Integer> elements, MinMaxPriorityQueue<Integer> q, Random random) { 830 while (!elements.isEmpty()) { 831 int selectedIndex = random.nextInt(elements.size()); 832 q.offer(elements.remove(selectedIndex)); 833 } 834 } 835 createOrderedList(int size)836 private ArrayList<Integer> createOrderedList(int size) { 837 ArrayList<Integer> elements = new ArrayList<>(size); 838 for (int i = 0; i < size; i++) { 839 elements.add(i); 840 } 841 return elements; 842 } 843 testIsEvenLevel()844 public void testIsEvenLevel() { 845 assertTrue(MinMaxPriorityQueue.isEvenLevel(0)); 846 assertFalse(MinMaxPriorityQueue.isEvenLevel(1)); 847 assertFalse(MinMaxPriorityQueue.isEvenLevel(2)); 848 assertTrue(MinMaxPriorityQueue.isEvenLevel(3)); 849 850 assertFalse(MinMaxPriorityQueue.isEvenLevel((1 << 10) - 2)); 851 assertTrue(MinMaxPriorityQueue.isEvenLevel((1 << 10) - 1)); 852 853 int i = 1 << 29; 854 assertTrue(MinMaxPriorityQueue.isEvenLevel(i - 2)); 855 assertFalse(MinMaxPriorityQueue.isEvenLevel(i - 1)); 856 assertFalse(MinMaxPriorityQueue.isEvenLevel(i)); 857 858 i = 1 << 30; 859 assertFalse(MinMaxPriorityQueue.isEvenLevel(i - 2)); 860 assertTrue(MinMaxPriorityQueue.isEvenLevel(i - 1)); 861 assertTrue(MinMaxPriorityQueue.isEvenLevel(i)); 862 863 // 1 << 31 is negative because of overflow, 1 << 31 - 1 is positive 864 // since isEvenLevel adds 1, we need to do - 2. 865 assertTrue(MinMaxPriorityQueue.isEvenLevel((1 << 31) - 2)); 866 assertTrue(MinMaxPriorityQueue.isEvenLevel(Integer.MAX_VALUE - 1)); 867 try { 868 MinMaxPriorityQueue.isEvenLevel((1 << 31) - 1); 869 fail("Should overflow"); 870 } catch (IllegalStateException expected) { 871 } 872 try { 873 MinMaxPriorityQueue.isEvenLevel(Integer.MAX_VALUE); 874 fail("Should overflow"); 875 } catch (IllegalStateException expected) { 876 } 877 try { 878 MinMaxPriorityQueue.isEvenLevel(1 << 31); 879 fail("Should be negative"); 880 } catch (IllegalStateException expected) { 881 } 882 try { 883 MinMaxPriorityQueue.isEvenLevel(Integer.MIN_VALUE); 884 fail("Should be negative"); 885 } catch (IllegalStateException expected) { 886 } 887 } 888 889 @GwtIncompatible // NullPointerTester testNullPointers()890 public void testNullPointers() { 891 NullPointerTester tester = new NullPointerTester(); 892 tester.testAllPublicConstructors(MinMaxPriorityQueue.class); 893 tester.testAllPublicStaticMethods(MinMaxPriorityQueue.class); 894 tester.testAllPublicInstanceMethods(MinMaxPriorityQueue.<String>create()); 895 } 896 insertIntoReplica(Map<Integer, AtomicInteger> replica, int newValue)897 private static void insertIntoReplica(Map<Integer, AtomicInteger> replica, int newValue) { 898 if (replica.containsKey(newValue)) { 899 replica.get(newValue).incrementAndGet(); 900 } else { 901 replica.put(newValue, new AtomicInteger(1)); 902 } 903 } 904 removeMinFromReplica( SortedMap<Integer, AtomicInteger> replica, int minValue)905 private static void removeMinFromReplica( 906 SortedMap<Integer, AtomicInteger> replica, int minValue) { 907 Integer replicatedMinValue = replica.firstKey(); 908 assertEquals(replicatedMinValue, (Integer) minValue); 909 removeFromReplica(replica, replicatedMinValue); 910 } 911 removeMaxFromReplica( SortedMap<Integer, AtomicInteger> replica, int maxValue)912 private static void removeMaxFromReplica( 913 SortedMap<Integer, AtomicInteger> replica, int maxValue) { 914 Integer replicatedMaxValue = replica.lastKey(); 915 assertTrue("maxValue is incorrect", replicatedMaxValue == maxValue); 916 removeFromReplica(replica, replicatedMaxValue); 917 } 918 removeFromReplica(Map<Integer, AtomicInteger> replica, int value)919 private static void removeFromReplica(Map<Integer, AtomicInteger> replica, int value) { 920 AtomicInteger numOccur = replica.get(value); 921 if (numOccur.decrementAndGet() == 0) { 922 replica.remove(value); 923 } 924 } 925 assertIntact(MinMaxPriorityQueue<?> q)926 private static void assertIntact(MinMaxPriorityQueue<?> q) { 927 if (!q.isIntact()) { 928 fail("State " + Arrays.toString(q.toArray())); 929 } 930 } 931 assertIntactUsingSeed(long seed, MinMaxPriorityQueue<?> q)932 private static void assertIntactUsingSeed(long seed, MinMaxPriorityQueue<?> q) { 933 if (!q.isIntact()) { 934 fail("Using seed " + seed + ". State " + Arrays.toString(q.toArray())); 935 } 936 } 937 assertIntactUsingStartedWith( Collection<?> startedWith, MinMaxPriorityQueue<?> q)938 private static void assertIntactUsingStartedWith( 939 Collection<?> startedWith, MinMaxPriorityQueue<?> q) { 940 if (!q.isIntact()) { 941 fail("Started with " + startedWith + ". State " + Arrays.toString(q.toArray())); 942 } 943 } 944 assertEqualsUsingSeed(long seed, Object expected, Object actual)945 private static void assertEqualsUsingSeed(long seed, Object expected, Object actual) { 946 if (!equal(actual, expected)) { 947 // fail(), but with the JUnit-supplied message. 948 assertEquals("Using seed " + seed, expected, actual); 949 } 950 } 951 assertEqualsUsingStartedWith( Collection<?> startedWith, Object expected, Object actual)952 private static void assertEqualsUsingStartedWith( 953 Collection<?> startedWith, Object expected, Object actual) { 954 if (!equal(actual, expected)) { 955 // fail(), but with the JUnit-supplied message. 956 assertEquals("Started with " + startedWith, expected, actual); 957 } 958 } 959 } 960