• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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