/*
* Copyright 2015 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.util.internal;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class DefaultPriorityQueueTest {
@Test
public void testPoll() {
PriorityQueue<TestElement> queue = new DefaultPriorityQueue<TestElement>(TestElementComparator.INSTANCE, 0);
assertEmptyQueue(queue);
TestElement a = new TestElement(5);
TestElement b = new TestElement(10);
TestElement c = new TestElement(2);
TestElement d = new TestElement(7);
TestElement e = new TestElement(6);
assertOffer(queue, a);
assertOffer(queue, b);
assertOffer(queue, c);
assertOffer(queue, d);
// Remove the first element
assertSame(c, queue.peek());
assertSame(c, queue.poll());
assertEquals(3, queue.size());
// Test that offering another element preserves the priority queue semantics.
assertOffer(queue, e);
assertEquals(4, queue.size());
assertSame(a, queue.peek());
assertSame(a, queue.poll());
assertEquals(3, queue.size());
// Keep removing the remaining elements
assertSame(e, queue.peek());
assertSame(e, queue.poll());
assertEquals(2, queue.size());
assertSame(d, queue.peek());
assertSame(d, queue.poll());
assertEquals(1, queue.size());
assertSame(b, queue.peek());
assertSame(b, queue.poll());
assertEmptyQueue(queue);
}
@Test
public void testClear() {
PriorityQueue<TestElement> queue = new DefaultPriorityQueue<TestElement>(TestElementComparator.INSTANCE, 0);
assertEmptyQueue(queue);
TestElement a = new TestElement(5);
TestElement b = new TestElement(10);
TestElement c = new TestElement(2);
TestElement d = new TestElement(6);
assertOffer(queue, a);
assertOffer(queue, b);
assertOffer(queue, c);
assertOffer(queue, d);
queue.clear();
assertEmptyQueue(queue);
// Test that elements can be re-inserted after the clear operation
assertOffer(queue, a);
assertSame(a, queue.peek());
assertOffer(queue, b);
assertSame(a, queue.peek());
assertOffer(queue, c);
assertSame(c, queue.peek());
assertOffer(queue, d);
assertSame(c, queue.peek());
}
@Test
public void testRemoval() {
testRemoval(false);
}
@Test
public void testRemovalTyped() {
testRemoval(true);
}
private void testRemoval(boolean typed) {
PriorityQueue<TestElement> queue = new DefaultPriorityQueue<TestElement>(TestElementComparator.INSTANCE, 4);
assertEmptyQueue(queue);
TestElement a = new TestElement(5);
TestElement b = new TestElement(10);
TestElement c = new TestElement(2);
TestElement d = new TestElement(6);
TestElement notInQueue = new TestElement(-1);
assertOffer(queue, a);
assertOffer(queue, b);
assertOffer(queue, c);
assertOffer(queue, d);
// Remove an element that isn't in the queue.
assertFalse(typed ? queue.removeTyped(notInQueue) : queue.remove(notInQueue));
assertSame(c, queue.peek());
assertEquals(4, queue.size());
// Remove the last element in the array, when the array is non-empty.
assertTrue(typed ? queue.removeTyped(b) : queue.remove(b));
assertSame(c, queue.peek());
assertEquals(3, queue.size());
// Re-insert the element after removal
assertOffer(queue, b);
assertSame(c, queue.peek());
assertEquals(4, queue.size());
// Repeat remove the last element in the array, when the array is non-empty.
assertTrue(typed ? queue.removeTyped(b) : queue.remove(b));
assertSame(c, queue.peek());
assertEquals(3, queue.size());
// Remove the head of the queue.
assertTrue(typed ? queue.removeTyped(c) : queue.remove(c));
assertSame(a, queue.peek());
assertEquals(2, queue.size());
assertTrue(typed ? queue.removeTyped(a) : queue.remove(a));
assertSame(d, queue.peek());
assertEquals(1, queue.size());
assertTrue(typed ? queue.removeTyped(d) : queue.remove(d));
assertEmptyQueue(queue);
}
@Test
public void testZeroInitialSize() {
PriorityQueue<TestElement> queue = new DefaultPriorityQueue<TestElement>(TestElementComparator.INSTANCE, 0);
assertEmptyQueue(queue);
TestElement e = new TestElement(1);
assertOffer(queue, e);
assertSame(e, queue.peek());
assertEquals(1, queue.size());
assertFalse(queue.isEmpty());
assertSame(e, queue.poll());
assertEmptyQueue(queue);
}
@Test
public void testPriorityChange() {
PriorityQueue<TestElement> queue = new DefaultPriorityQueue<TestElement>(TestElementComparator.INSTANCE, 0);
assertEmptyQueue(queue);
TestElement a = new TestElement(10);
TestElement b = new TestElement(20);
TestElement c = new TestElement(30);
TestElement d = new TestElement(25);
TestElement e = new TestElement(23);
TestElement f = new TestElement(15);
queue.add(a);
queue.add(b);
queue.add(c);
queue.add(d);
queue.add(e);
queue.add(f);
e.value = 35;
queue.priorityChanged(e);
a.value = 40;
queue.priorityChanged(a);
a.value = 31;
queue.priorityChanged(a);
d.value = 10;
queue.priorityChanged(d);
f.value = 5;
queue.priorityChanged(f);
List<TestElement> expectedOrderList = new ArrayList<TestElement>(queue.size());
expectedOrderList.addAll(Arrays.asList(a, b, c, d, e, f));
Collections.sort(expectedOrderList, TestElementComparator.INSTANCE);
assertEquals(expectedOrderList.size(), queue.size());
assertEquals(expectedOrderList.isEmpty(), queue.isEmpty());
Iterator<TestElement> itr = expectedOrderList.iterator();
while (itr.hasNext()) {
TestElement next = itr.next();
TestElement poll = queue.poll();
assertEquals(next, poll);
itr.remove();
assertEquals(expectedOrderList.size(), queue.size());
assertEquals(expectedOrderList.isEmpty(), queue.isEmpty());
}
}
private static void assertOffer(PriorityQueue<TestElement> queue, TestElement a) {
assertTrue(queue.offer(a));
assertTrue(queue.contains(a));
assertTrue(queue.containsTyped(a));
try { // An element can not be inserted more than 1 time.
queue.offer(a);
fail();
} catch (IllegalArgumentException ignored) {
// ignored
}
}
private static void assertEmptyQueue(PriorityQueue<TestElement> queue) {
assertNull(queue.peek());
assertNull(queue.poll());
assertEquals(0, queue.size());
assertTrue(queue.isEmpty());
}
private static final class TestElementComparator implements Comparator<TestElement> {
static final TestElementComparator INSTANCE = new TestElementComparator();
private TestElementComparator() {
}
@Override
public int compare(TestElement o1, TestElement o2) {
return o1.value - o2.value;
}
}
private static final class TestElement implements PriorityQueueNode {
int value;
private int priorityQueueIndex = INDEX_NOT_IN_QUEUE;
TestElement(int value) {
this.value = value;
}
@Override
public boolean equals(Object o) {
return o instanceof TestElement && ((TestElement) o).value == value;
}
@Override
public int hashCode() {
return value;
}
@Override
public int priorityQueueIndex(DefaultPriorityQueue queue) {
return priorityQueueIndex;
}
@Override
public void priorityQueueIndex(DefaultPriorityQueue queue, int i) {
priorityQueueIndex = i;
}
}
}