/* * Copyright (C) 2006-2016 DLR, Germany * * All rights reserved * * http://www.rcenvironment.de/ */ package de.rcenvironment.core.component.execution.internal; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.util.HashSet; import java.util.Set; import java.util.UUID; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; import org.easymock.EasyMock; import org.junit.Test; import de.rcenvironment.core.component.api.DistributedComponentKnowledge; import de.rcenvironment.core.component.api.DistributedComponentKnowledgeService; import de.rcenvironment.core.component.model.api.ComponentInstallation; import de.rcenvironment.core.toolkitbridge.transitional.ConcurrencyUtils; import de.rcenvironment.toolkit.modules.concurrency.api.AsyncTaskService; /** * Tests for {@link ComponentExecutionPermitsServiceImpl}. * * @author Doreen Seider * */ public class ComponentExecutionPermitsServiceImplTest { private static final int TEST_TIMEOUT = 2000; private static final int WAIT_INTERVAL = 200; private static final String COMPONENT_IDENTIFIER_1 = "comp-id-1"; private static final String COMPONENT_IDENTIFIER_2 = "comp-id-2"; /** * Tests if permits for component execution can be acquired and released as expected. * * @throws ExecutionException on error * @throws InterruptedException on error */ @Test(timeout = TEST_TIMEOUT) public void testAcquireAndReleasePermits() throws InterruptedException, ExecutionException { DistributedComponentKnowledgeService componentKnowledgeServiceMock = createDistributedComponentKnowledgeServiceMock(createDistributedComponentKnowledgeMock(createSetOfComponentInstallations( new String[] { COMPONENT_IDENTIFIER_1, COMPONENT_IDENTIFIER_2 }, new int[] { 2, 1 }))); final ComponentExecutionPermitsServiceImpl componentExecutionPermitsService = new ComponentExecutionPermitsServiceImpl(); componentExecutionPermitsService.bindDistributedComponentKnowledgeService(componentKnowledgeServiceMock); final AtomicInteger order1 = new AtomicInteger(0); final AtomicInteger order2 = new AtomicInteger(0); componentExecutionPermitsService.acquire(COMPONENT_IDENTIFIER_1, UUID.randomUUID().toString()).get(); componentExecutionPermitsService.acquire(COMPONENT_IDENTIFIER_2, UUID.randomUUID().toString()).get(); componentExecutionPermitsService.acquire(COMPONENT_IDENTIFIER_1, UUID.randomUUID().toString()).get(); AsyncTaskService threadPool = ConcurrencyUtils.getAsyncTaskService(); Future<Integer> acquireTask1 = threadPool.submit(new Callable<Integer>() { @Override public Integer call() throws Exception { if (componentExecutionPermitsService.acquire(COMPONENT_IDENTIFIER_1, UUID.randomUUID().toString()).get()) { synchronized (order1) { return order1.incrementAndGet(); } } return order1.get(); } }); Future<Integer> acquireTask2 = threadPool.submit(new Callable<Integer>() { @Override public Integer call() throws Exception { if (componentExecutionPermitsService.acquire(COMPONENT_IDENTIFIER_2, UUID.randomUUID().toString()).get()) { synchronized (order2) { return order2.incrementAndGet(); } } return order2.get(); } }); Thread.sleep(WAIT_INTERVAL); synchronized (order2) { componentExecutionPermitsService.release(COMPONENT_IDENTIFIER_2); order2.incrementAndGet(); } assertEquals(2, acquireTask2.get().intValue()); synchronized (order1) { componentExecutionPermitsService.release(COMPONENT_IDENTIFIER_1); order1.incrementAndGet(); } assertEquals(2, acquireTask1.get().intValue()); componentExecutionPermitsService.release(COMPONENT_IDENTIFIER_1); componentExecutionPermitsService.acquire(COMPONENT_IDENTIFIER_1, UUID.randomUUID().toString()).get(); } /** * Tests if permits for component execution can be acquired and released as expected. * * @throws ExecutionException on error * @throws InterruptedException on error */ @Test(timeout = TEST_TIMEOUT) public void testCancelAcquiringPermits() throws InterruptedException, ExecutionException { DistributedComponentKnowledgeService componentKnowledgeServiceMock = createDistributedComponentKnowledgeServiceMock(createDistributedComponentKnowledgeMock(createSetOfComponentInstallations( new String[] { COMPONENT_IDENTIFIER_1 }, new int[] { 1 }))); final ComponentExecutionPermitsServiceImpl componentExecutionPermitsService = new ComponentExecutionPermitsServiceImpl(); componentExecutionPermitsService.bindDistributedComponentKnowledgeService(componentKnowledgeServiceMock); assertTrue(componentExecutionPermitsService.acquire(COMPONENT_IDENTIFIER_1, UUID.randomUUID().toString()).get()); Future<Boolean> acquireTask = componentExecutionPermitsService.acquire(COMPONENT_IDENTIFIER_1, UUID.randomUUID().toString()); acquireTask.cancel(true); componentExecutionPermitsService.release(COMPONENT_IDENTIFIER_1); assertTrue(componentExecutionPermitsService.acquire(COMPONENT_IDENTIFIER_1, UUID.randomUUID().toString()).get()); } /** * Tests if parameter for maximum parallel executions allowed is increased concurrently. That means, if permit request * (ComponentExecutionPermitsServiceImpl#acquire()) is blocked and the maximum parallel executions allowed are increased, the block is * expected to disappear immediately. * * @throws ExecutionException on error * @throws InterruptedException on error */ @Test(timeout = TEST_TIMEOUT) public void testIfMaxParallelExecutionParamIsIncreasedConcurrently() throws InterruptedException, ExecutionException { DistributedComponentKnowledge componentKnowledgeMock = createDistributedComponentKnowledgeMock(createSetOfComponentInstallations( new String[] { COMPONENT_IDENTIFIER_1 }, new int[] { 1 })); DistributedComponentKnowledgeService componentKnowledgeServiceMock = createDistributedComponentKnowledgeServiceMock(componentKnowledgeMock); final ComponentExecutionPermitsServiceImpl componentExecutionPermitsService = new ComponentExecutionPermitsServiceImpl(); componentExecutionPermitsService.bindDistributedComponentKnowledgeService(componentKnowledgeServiceMock); final AtomicInteger order = new AtomicInteger(0); componentExecutionPermitsService.acquire(COMPONENT_IDENTIFIER_1, UUID.randomUUID().toString()).get(); AsyncTaskService threadPool = ConcurrencyUtils.getAsyncTaskService(); Future<Integer> acquireTask = threadPool.submit(new Callable<Integer>() { @Override public Integer call() throws Exception { componentExecutionPermitsService.acquire(COMPONENT_IDENTIFIER_1, UUID.randomUUID().toString()).get(); synchronized (order) { return order.incrementAndGet(); } } }); Thread.sleep(WAIT_INTERVAL); synchronized (order) { componentExecutionPermitsService.onDistributedComponentKnowledgeChanged(componentKnowledgeMock); order.incrementAndGet(); } Thread.sleep(WAIT_INTERVAL); DistributedComponentKnowledge updatedComponentKnowledgeMock = EasyMock.createStrictMock(DistributedComponentKnowledge.class); EasyMock.expect(updatedComponentKnowledgeMock.getAllInstallations()).andReturn(createSetOfComponentInstallations( new String[] { COMPONENT_IDENTIFIER_1 }, new int[] { 2 })).anyTimes(); EasyMock.replay(updatedComponentKnowledgeMock); synchronized (order) { componentExecutionPermitsService.onDistributedComponentKnowledgeChanged(updatedComponentKnowledgeMock); order.incrementAndGet(); } assertEquals(3, acquireTask.get().intValue()); } /** * Tests if parameter for maximum parallel executions allowed is decreased concurrently. That means, if permit request ( * {@link ComponentExecutionPermitsService#acquire(String, String)}) is blocked and the maximum parallel executions allowed are * increased, the block is expected to disappear immediately. * * @throws ExecutionException on error * @throws InterruptedException on error */ @Test(timeout = TEST_TIMEOUT) public void testIfMaxParallelExecutionParamIsDecreasedConcurrently() throws InterruptedException, ExecutionException { DistributedComponentKnowledge componentKnowledgeMock = createDistributedComponentKnowledgeMock(createSetOfComponentInstallations( new String[] { COMPONENT_IDENTIFIER_1 }, new int[] { 2 })); DistributedComponentKnowledgeService componentKnowledgeServiceMock = createDistributedComponentKnowledgeServiceMock(componentKnowledgeMock); final ComponentExecutionPermitsServiceImpl componentExecutionPermitsService = new ComponentExecutionPermitsServiceImpl(); componentExecutionPermitsService.bindDistributedComponentKnowledgeService(componentKnowledgeServiceMock); final AtomicInteger order = new AtomicInteger(0); componentExecutionPermitsService.acquire(COMPONENT_IDENTIFIER_1, UUID.randomUUID().toString()).get(); componentExecutionPermitsService.acquire(COMPONENT_IDENTIFIER_1, UUID.randomUUID().toString()).get(); AsyncTaskService threadPool = ConcurrencyUtils.getAsyncTaskService(); Future<Integer> acquireTask = threadPool.submit(new Callable<Integer>() { @Override public Integer call() throws Exception { componentExecutionPermitsService.acquire(COMPONENT_IDENTIFIER_1, UUID.randomUUID().toString()).get(); synchronized (order) { return order.incrementAndGet(); } } }); Thread.sleep(WAIT_INTERVAL); synchronized (order) { componentExecutionPermitsService.onDistributedComponentKnowledgeChanged(componentKnowledgeMock); order.incrementAndGet(); } Thread.sleep(WAIT_INTERVAL); DistributedComponentKnowledge updatedComponentKnowledgeMock = EasyMock.createStrictMock(DistributedComponentKnowledge.class); EasyMock.expect(updatedComponentKnowledgeMock.getAllInstallations()).andReturn(createSetOfComponentInstallations( new String[] { COMPONENT_IDENTIFIER_1 }, new int[] { 1 })).anyTimes(); EasyMock.replay(updatedComponentKnowledgeMock); synchronized (order) { componentExecutionPermitsService.onDistributedComponentKnowledgeChanged(updatedComponentKnowledgeMock); order.incrementAndGet(); } Thread.sleep(WAIT_INTERVAL); synchronized (order) { componentExecutionPermitsService.release(COMPONENT_IDENTIFIER_1); order.incrementAndGet(); } Thread.sleep(WAIT_INTERVAL); synchronized (order) { componentExecutionPermitsService.release(COMPONENT_IDENTIFIER_1); order.incrementAndGet(); } assertEquals(5, acquireTask.get().intValue()); } /** * Performs more releases than acquires and checks if this doesn't affect the maximum amounts of execution permits allowed. * * @throws ExecutionException on error * @throws InterruptedException on error */ @Test(timeout = TEST_TIMEOUT) public void testIfMultipleReleasesDoesNotIncreaseTheMaximumAmountsOfPermitsAllowed() throws InterruptedException, ExecutionException { DistributedComponentKnowledgeService componentKnowledgeServiceMock = createDistributedComponentKnowledgeServiceMock(createDistributedComponentKnowledgeMock(createSetOfComponentInstallations( new String[] { COMPONENT_IDENTIFIER_1 }, new int[] { 1 }))); final ComponentExecutionPermitsServiceImpl componentExecutionPermitsService = new ComponentExecutionPermitsServiceImpl(); componentExecutionPermitsService.bindDistributedComponentKnowledgeService(componentKnowledgeServiceMock); final AtomicInteger order = new AtomicInteger(0); componentExecutionPermitsService.release(COMPONENT_IDENTIFIER_1); componentExecutionPermitsService.release(COMPONENT_IDENTIFIER_1); componentExecutionPermitsService.release(COMPONENT_IDENTIFIER_1); componentExecutionPermitsService.acquire(COMPONENT_IDENTIFIER_1, UUID.randomUUID().toString()).get(); AsyncTaskService threadPool = ConcurrencyUtils.getAsyncTaskService(); Future<Integer> acquireTask = threadPool.submit(new Callable<Integer>() { @Override public Integer call() throws Exception { componentExecutionPermitsService.acquire(COMPONENT_IDENTIFIER_1, UUID.randomUUID().toString()).get(); synchronized (order) { return order.incrementAndGet(); } } }); Thread.sleep(WAIT_INTERVAL); synchronized (order) { componentExecutionPermitsService.release(COMPONENT_IDENTIFIER_1); order.incrementAndGet(); } assertEquals(2, acquireTask.get().intValue()); } /** * Sets the maximum parallel execution allowed to 0 and awaits that no permit is granted when * {@link ComponentExecutionPermitsService#acquire(String, String)} is called. * * @throws ExecutionException on error * @throws InterruptedException on error */ @Test(timeout = TEST_TIMEOUT) public void testIfNoPermitIsGrantedMaximumAmountOfParallelExecutionsIsLeesOrEqualZero() throws InterruptedException, ExecutionException { final int minusFive = -5; DistributedComponentKnowledgeService componentKnowledgeServiceMock = createDistributedComponentKnowledgeServiceMock(createDistributedComponentKnowledgeMock(createSetOfComponentInstallations( new String[] { COMPONENT_IDENTIFIER_1, COMPONENT_IDENTIFIER_2 }, new int[] { 0, minusFive }))); final ComponentExecutionPermitsServiceImpl componentExecutionPermitsService = new ComponentExecutionPermitsServiceImpl(); componentExecutionPermitsService.bindDistributedComponentKnowledgeService(componentKnowledgeServiceMock); AsyncTaskService threadPool = ConcurrencyUtils.getAsyncTaskService(); Future<Void> acquireTask1 = threadPool.submit(new Callable<Void>() { @Override public Void call() throws Exception { componentExecutionPermitsService.acquire(COMPONENT_IDENTIFIER_1, UUID.randomUUID().toString()).get(); return null; } }); Future<Void> acquireTask2 = threadPool.submit(new Callable<Void>() { @Override public Void call() throws Exception { componentExecutionPermitsService.acquire(COMPONENT_IDENTIFIER_2, UUID.randomUUID().toString()).get(); return null; } }); final int timeoutInMillis = 200; try { acquireTask1.get(timeoutInMillis, TimeUnit.MILLISECONDS); fail("Timeout expected."); } catch (TimeoutException e) { assertTrue(true); } try { acquireTask2.get(timeoutInMillis, TimeUnit.MILLISECONDS); fail("Timeout expected."); } catch (TimeoutException e) { assertTrue(true); } } /** * Performs more releases than acquires and checks if this doesn't affect the maximum amounts of execution permits allowed. * * @throws ExecutionException on error * @throws InterruptedException on error */ @Test(timeout = TEST_TIMEOUT) public void testUnlimitedParallelExecutionByDefiningNoParamForMaximumParallelExecution() throws InterruptedException, ExecutionException { DistributedComponentKnowledgeService componentKnowledgeServiceMock = createDistributedComponentKnowledgeServiceMock(createDistributedComponentKnowledgeMock(createSetOfComponentInstallations( new String[] { COMPONENT_IDENTIFIER_1 }, new int[] { }))); final ComponentExecutionPermitsServiceImpl componentExecutionPermitsService = new ComponentExecutionPermitsServiceImpl(); componentExecutionPermitsService.bindDistributedComponentKnowledgeService(componentKnowledgeServiceMock); final int amountOfAcquires = 100; for (int i = 0; i < amountOfAcquires; i++) { componentExecutionPermitsService.acquire(COMPONENT_IDENTIFIER_1, UUID.randomUUID().toString()).get(); } } private DistributedComponentKnowledgeService createDistributedComponentKnowledgeServiceMock( DistributedComponentKnowledge componentKnowledge) { DistributedComponentKnowledgeService componentKnowledgeServiceMock = EasyMock.createNiceMock( DistributedComponentKnowledgeService.class); EasyMock.expect(componentKnowledgeServiceMock.getCurrentComponentKnowledge()).andReturn(componentKnowledge).anyTimes(); EasyMock.replay(componentKnowledgeServiceMock); return componentKnowledgeServiceMock; } private DistributedComponentKnowledge createDistributedComponentKnowledgeMock(Set<ComponentInstallation> componentInstallations) { DistributedComponentKnowledge componentKnowledgeMock = EasyMock.createStrictMock(DistributedComponentKnowledge.class); EasyMock.expect(componentKnowledgeMock.getAllInstallations()).andReturn(componentInstallations).anyTimes(); EasyMock.replay(componentKnowledgeMock); return componentKnowledgeMock; } private ComponentInstallation createComponentInstallationMock(String compId, Integer maxParallelExecutions) { ComponentInstallation componentInstallationMock = EasyMock.createNiceMock(ComponentInstallation.class); EasyMock.expect(componentInstallationMock.getInstallationId()).andReturn(compId).anyTimes(); EasyMock.expect(componentInstallationMock.getMaximumCountOfParallelInstances()).andReturn(maxParallelExecutions).anyTimes(); EasyMock.replay(componentInstallationMock); return componentInstallationMock; } private Set<ComponentInstallation> createSetOfComponentInstallations(String[] compIds, int[] maxParallelExecutions) { Set<ComponentInstallation> componentInstallations = new HashSet<>(); for (int i = 0; i < compIds.length; i++) { if (i < maxParallelExecutions.length) { componentInstallations.add(createComponentInstallationMock(compIds[i], maxParallelExecutions[i])); } else { componentInstallations.add(createComponentInstallationMock(compIds[i], null)); } } return componentInstallations; } }