/*
* Copyright (c) 2008-2017, Hazelcast, Inc. All Rights Reserved.
*
* Licensed 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 com.hazelcast.spi.impl.operationexecutor.impl;
import com.hazelcast.test.HazelcastSerialClassRunner;
import com.hazelcast.test.HazelcastTestSupport;
import com.hazelcast.test.annotation.QuickTest;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
@RunWith(HazelcastSerialClassRunner.class)
@Category(QuickTest.class)
public class DefaultOperationQueueTest extends HazelcastTestSupport {
private DefaultOperationQueue operationQueue;
private ArrayBlockingQueue<Object> normalQueue;
private ArrayBlockingQueue<Object> priorityQueue;
@Before
public void setup() {
normalQueue = new ArrayBlockingQueue<Object>(100);
priorityQueue = new ArrayBlockingQueue<Object>(100);
operationQueue = new DefaultOperationQueue(normalQueue, priorityQueue);
}
// ================== add =====================
@Test(expected = NullPointerException.class)
public void add_whenNull() {
operationQueue.add(null, false);
}
@Test
public void add_whenPriority() throws InterruptedException {
Object task = new Object();
operationQueue.add(task, true);
assertEquals(1, operationQueue.prioritySize());
assertEquals(1, operationQueue.normalSize());
assertEquals(2, operationQueue.size());
assertEquals(1, priorityQueue.size());
assertEquals(1, normalQueue.size());
assertSame(task, priorityQueue.iterator().next());
assertSame(DefaultOperationQueue.TRIGGER_TASK, normalQueue.iterator().next());
}
@Test
public void add_whenNormal() {
Object task = new Object();
operationQueue.add(task, false);
assertContent(normalQueue, task);
assertEmpty(priorityQueue);
assertEquals(0, operationQueue.prioritySize());
assertEquals(1, operationQueue.normalSize());
assertEquals(1, operationQueue.size());
}
// ================== take =====================
@Test
public void take_whenPriorityItemAvailable() throws InterruptedException {
Object task1 = "task1";
Object task2 = "task2";
Object task3 = "task3";
operationQueue.add(task1, true);
operationQueue.add(task2, true);
operationQueue.add(task3, true);
assertSame(task1, operationQueue.take(false));
assertSame(task2, operationQueue.take(false));
assertSame(task3, operationQueue.take(false));
assertEquals(3, operationQueue.size());
assertEquals(0, operationQueue.prioritySize());
assertEquals(3, operationQueue.normalSize());
}
/**
* It could be that in the low priority query there are a bunch of useless trigger tasks preceding a regular tasks.
*/
@Test
public void take_whenLowPriority_andManyPrecedingTriggerTasks() throws InterruptedException {
Object task1 = "task1";
Object task2 = "task2";
Object task3 = "task3";
Object task4 = "task4";
operationQueue.add(task1, true);
operationQueue.add(task2, true);
operationQueue.add(task3, true);
operationQueue.add(task4, false);
assertSame(task1, operationQueue.take(false));
assertSame(task2, operationQueue.take(false));
assertSame(task3, operationQueue.take(false));
// at this moment there are 3 trigger tasks and 1 normal task
assertEquals(4, operationQueue.size());
// when we take the item
assertSame(task4, operationQueue.take(false));
// all the trigger tasks are drained
assertEquals(0, operationQueue.size());
assertEquals(0, operationQueue.prioritySize());
assertEquals(0, operationQueue.normalSize());
}
@Test
public void take_whenRegularItemAvailable() throws InterruptedException {
Object task1 = "task1";
Object task2 = "task2";
Object task3 = "task3";
operationQueue.add(task1, false);
operationQueue.add(task2, false);
operationQueue.add(task3, false);
assertSame(task1, operationQueue.take(false));
assertSame(task2, operationQueue.take(false));
assertSame(task3, operationQueue.take(false));
assertEquals(0, operationQueue.size());
assertEquals(0, operationQueue.prioritySize());
assertEquals(0, operationQueue.normalSize());
}
@Test
public void take_whenPriorityAndRegularItemAvailable() throws InterruptedException {
Object task1 = "task1";
Object task2 = "task2";
Object task3 = "task3";
operationQueue.add(task1, true);
operationQueue.add(task2, false);
operationQueue.add(task3, true);
assertSame(task1, operationQueue.take(true));
assertSame(task3, operationQueue.take(true));
assertEquals(0, operationQueue.prioritySize());
}
@Test
public void take_whenPriority_andNoItemAvailable_thenBlockTillItemAvailable() throws InterruptedException {
final Object task1 = "task1";
final Object task2 = "task2";
operationQueue.add(task1, false);
spawn(new Runnable() {
@Override
public void run() {
sleepSeconds(4);
operationQueue.add(task2, true);
}
});
assertSame(task2, operationQueue.take(true));
assertEquals(0, operationQueue.prioritySize());
}
@Test
public void take_priorityIsRetrievedFirst() throws InterruptedException {
Object priorityTask1 = "priority1";
Object priorityTask2 = "priority2";
Object priorityTask3 = "priority4";
Object normalTask1 = "normalTask1";
Object normalTask2 = "normalTask2";
Object normalTask3 = "normalTask3";
operationQueue.add(priorityTask1, true);
operationQueue.add(normalTask1, false);
operationQueue.add(normalTask2, false);
operationQueue.add(priorityTask2, true);
operationQueue.add(normalTask3, false);
operationQueue.add(priorityTask3, true);
assertSame(priorityTask1, operationQueue.take(false));
assertSame(priorityTask2, operationQueue.take(false));
assertSame(priorityTask3, operationQueue.take(false));
assertSame(normalTask1, operationQueue.take(false));
assertSame(normalTask2, operationQueue.take(false));
assertSame(normalTask3, operationQueue.take(false));
assertEmpty(priorityQueue);
//assertContent(normalQueue, DefaultOperationQueue.TRIGGER_TASK);
}
public void assertEmpty(Queue<Object> q) {
assertEquals("expecting an empty operationQueue, but the operationQueue is:" + q, 0, q.size());
}
public void assertContent(Queue<Object> q, Object... expected) {
List<Object> actual = new LinkedList<Object>(q);
assertEquals(Arrays.asList(expected), actual);
}
}