/* * 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.util.executor; import com.hazelcast.spi.ExecutionService; import com.hazelcast.spi.NodeEngine; import com.hazelcast.test.AssertTask; import com.hazelcast.test.HazelcastParallelClassRunner; import com.hazelcast.test.annotation.ParallelTest; import com.hazelcast.test.annotation.QuickTest; import org.junit.After; 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.Collections; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.LockSupport; import static com.hazelcast.test.HazelcastTestSupport.assertOpenEventually; import static com.hazelcast.test.HazelcastTestSupport.assertTrueEventually; import static com.hazelcast.test.HazelcastTestSupport.randomString; import static com.hazelcast.util.FutureUtil.checkAllDone; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @RunWith(HazelcastParallelClassRunner.class) @Category({QuickTest.class, ParallelTest.class}) public class CachedExecutorServiceDelegateTest { private static final String NAME = "test-executor"; private ManagedExecutorService cachedExecutorService; private NodeEngine nodeEngine; @Before public void setup() { cachedExecutorService = new NamedThreadPoolExecutor("test", 0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), Executors.defaultThreadFactory()); ExecutionService executionService = mock(ExecutionService.class); when(executionService.getExecutor(ExecutionService.ASYNC_EXECUTOR)) .thenReturn(cachedExecutorService); nodeEngine = mock(NodeEngine.class); when(nodeEngine.getExecutionService()).thenReturn(executionService); } @After public void cleanup() { cachedExecutorService.shutdown(); } @Test(expected = IllegalArgumentException.class) public void nonPositiveMaxPoolSize() { newManagedExecutorService(-1, 1); } @Test(expected = IllegalArgumentException.class) public void nonPositiveQueueCapacity() { newManagedExecutorService(1, -1); } @Test public void getName() throws Exception { ManagedExecutorService executor = newManagedExecutorService(); assertEquals(NAME, executor.getName()); } @Test public void getMaximumPoolSize() throws Exception { int maxPoolSize = 123; assertEquals(maxPoolSize, newManagedExecutorService(maxPoolSize, 1).getMaximumPoolSize()); } @Test public void getPoolSize_whenNoTasksSubmitted() throws Exception { assertEquals(0, newManagedExecutorService().getPoolSize()); } @Test public void getPoolSize_whenTaskSubmitted() throws Exception { int maxPoolSize = 3; ManagedExecutorService executorService = newManagedExecutorService(maxPoolSize, 100); final CountDownLatch startLatch = new CountDownLatch(maxPoolSize); final CountDownLatch finishLatch = new CountDownLatch(1); try { for (int i = 0; i < maxPoolSize * 2; i++) { executorService.execute(new Runnable() { @Override public void run() { startLatch.countDown(); assertOpenEventually(finishLatch); } }); } assertOpenEventually(startLatch); assertEquals(maxPoolSize, executorService.getPoolSize()); } finally { finishLatch.countDown(); } } @Test public void getQueueSize_whenNoTasksSubmitted() throws Exception { assertEquals(0, newManagedExecutorService().getQueueSize()); } @Test public void getQueueSize_whenTaskSubmitted() throws Exception { int queueSize = 10; ManagedExecutorService executorService = newManagedExecutorService(1, queueSize); CountDownLatch finishLatch = startLongRunningTask(executorService); try { executeNopTask(executorService); assertEquals(1, executorService.getQueueSize()); assertEquals(1, executorService.getQueueSize()); } finally { finishLatch.countDown(); } } @Test public void getRemainingQueueCapacity_whenNoTasksSubmitted() throws Exception { int queueSize = 123; assertEquals(queueSize, newManagedExecutorService(1, queueSize).getRemainingQueueCapacity()); } @Test public void getRemainingQueueCapacity_whenTaskSubmitted() throws Exception { int queueSize = 10; ManagedExecutorService executorService = newManagedExecutorService(1, queueSize); CountDownLatch finishLatch = startLongRunningTask(executorService); try { executeNopTask(executorService); assertEquals(queueSize - 1, executorService.getRemainingQueueCapacity()); } finally { finishLatch.countDown(); } } @Test public void getCompletedTaskCount_whenNoTasksSubmitted() throws Exception { assertEquals(0, newManagedExecutorService().getCompletedTaskCount()); } @Test public void getCompletedTaskCount_whenTasksSubmitted() throws Exception { final int taskCount = 10; final ManagedExecutorService executorService = newManagedExecutorService(); for (int i = 0; i < taskCount; i++) { executeNopTask(executorService); } assertTrueEventually(new AssertTask() { @Override public void run() throws Exception { assertEquals(taskCount, executorService.getCompletedTaskCount()); } }); } @Test public void execute() throws Exception { final int taskCount = 10; ManagedExecutorService executorService = newManagedExecutorService(1, taskCount); final CountDownLatch latch = new CountDownLatch(taskCount); for (int i = 0; i < taskCount; i++) { executorService.execute(new Runnable() { @Override public void run() { latch.countDown(); } }); } assertOpenEventually(latch); } @Test(expected = RejectedExecutionException.class) public void execute_rejected_whenShutdown() throws Exception { ManagedExecutorService executorService = newManagedExecutorService(); executorService.shutdown(); executeNopTask(executorService); } @Test public void submitRunnable() throws Exception { final int taskCount = 10; ManagedExecutorService executorService = newManagedExecutorService(1, taskCount); Future[] futures = new Future[taskCount]; for (int i = 0; i < taskCount; i++) { futures[i] = executorService.submit(new Runnable() { @Override public void run() { } }); } checkAllDone(Arrays.asList(futures)); } @Test public void submitCallable() throws Exception { final int taskCount = 10; ManagedExecutorService executorService = newManagedExecutorService(1, taskCount); final String result = randomString(); Future[] futures = new Future[taskCount]; for (int i = 0; i < taskCount; i++) { futures[i] = executorService.submit(new Callable() { @Override public Object call() throws Exception { return result; } }); } checkAllDone(Arrays.asList(futures)); for (Future future : futures) { assertEquals(result, future.get()); } } @Test public void submitRunnable_withResult() throws Exception { final int taskCount = 10; ManagedExecutorService executorService = newManagedExecutorService(1, taskCount); final String result = randomString(); Future[] futures = new Future[taskCount]; for (int i = 0; i < taskCount; i++) { futures[i] = executorService.submit(new Runnable() { @Override public void run() { } }, result); } checkAllDone(Arrays.asList(futures)); for (Future future : futures) { assertEquals(result, future.get()); } } @Test public void shutdown() throws Exception { ManagedExecutorService executorService = newManagedExecutorService(); Future<Object> future = executorService.submit(new Callable<Object>() { @Override public Object call() throws Exception { LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1)); return null; } }); executorService.shutdown(); assertTrue(executorService.isShutdown()); future.get(); } @Test public void shutdownNow() throws Exception { ManagedExecutorService executorService = newManagedExecutorService(); CountDownLatch finishLatch = startLongRunningTask(executorService); try { Future<Object> future = executorService.submit(new Callable<Object>() { @Override public Object call() throws Exception { return null; } }); List<Runnable> tasks = executorService.shutdownNow(); assertTrue(executorService.isShutdown()); assertEquals(1, tasks.size()); try { future.get(); } catch (CancellationException expected) { } } finally { finishLatch.countDown(); } } public void isShutdown_whenRunning() throws Exception { assertFalse(newManagedExecutorService().isShutdown()); } @Test public void isShutdown() throws Exception { ManagedExecutorService executorService = newManagedExecutorService(); executorService.shutdown(); assertTrue(executorService.isShutdown()); } @Test public void isTerminated() throws Exception { ManagedExecutorService executorService = newManagedExecutorService(); executorService.shutdown(); assertTrue(executorService.isTerminated()); } @Test public void isTerminated_whenRunning() throws Exception { assertFalse(newManagedExecutorService().isTerminated()); } @Test(expected = UnsupportedOperationException.class) public void awaitTermination() throws Exception { newManagedExecutorService().awaitTermination(1, TimeUnit.SECONDS); } @Test(expected = UnsupportedOperationException.class) public void invokeAll() throws Exception { newManagedExecutorService().invokeAll(Collections.singleton(new Callable<Object>() { @Override public Object call() throws Exception { return null; } })); } @Test(expected = UnsupportedOperationException.class) public void invokeAll_withTimeout() throws Exception { newManagedExecutorService().invokeAll(Collections.singleton(new Callable<Object>() { @Override public Object call() throws Exception { return null; } }), 1, TimeUnit.SECONDS); } @Test(expected = UnsupportedOperationException.class) public void invokeAny() throws Exception { newManagedExecutorService().invokeAny(Collections.singleton(new Callable<Object>() { @Override public Object call() throws Exception { return null; } })); } @Test(expected = UnsupportedOperationException.class) public void invokeAny_withTimeout() throws Exception { newManagedExecutorService().invokeAny(Collections.singleton(new Callable<Object>() { @Override public Object call() throws Exception { return null; } }), 1, TimeUnit.SECONDS); } private CountDownLatch startLongRunningTask(ExecutorService executorService) { final CountDownLatch startLatch = new CountDownLatch(1); final CountDownLatch finishLatch = new CountDownLatch(1); executorService.execute(new Runnable() { @Override public void run() { startLatch.countDown(); assertOpenEventually(finishLatch); } }); assertOpenEventually(startLatch); return finishLatch; } private void executeNopTask(ExecutorService executorService) { executorService.execute(new Runnable() { @Override public void run() { } }); } private ManagedExecutorService newManagedExecutorService() { return newManagedExecutorService(1, 10); } private ManagedExecutorService newManagedExecutorService(int maxPoolSize, int queueCapacity) { return new CachedExecutorServiceDelegate(nodeEngine, NAME, cachedExecutorService, maxPoolSize, queueCapacity); } }