package de.uni_goettingen.sub.commons.ocr.abbyy.server.multiuser; import static org.junit.Assert.*; import static org.mockito.Mockito.*; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import org.junit.Before; import org.junit.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import com.hazelcast.core.HazelcastInstance; import com.hazelcast.core.ILock; import de.uni_goettingen.sub.commons.ocr.abbyy.server.AbbyyProcess; import de.uni_goettingen.sub.commons.ocr.api.OcrPriority; public class HazelcastExecutorTest { private HazelcastExecutor executorSutA; private AbbyyProcess processMockA1 = mock(AbbyyProcess.class); private AbbyyProcess processMockA2 = mock(AbbyyProcess.class); private HazelcastExecutor executorSutB; private AbbyyProcess processMockB1 = mock(AbbyyProcess.class); private AbbyyProcess processMockB2 = mock(AbbyyProcess.class); private HazelcastExecutor executorSutC; private AbbyyProcess processMockC1 = mock(AbbyyProcess.class); private HazelcastInstance hazelMock = mock(HazelcastInstance.class); private Lock lock = new ReentrantLock(); private Condition conditionForExecution = lock.newCondition(); private Map<String, AbbyyProcess> queuedProcesses = new HashMap<String, AbbyyProcess>(); private Set<String> runningProcesses = new HashSet<String>(); private final static boolean ENOUGH_SPACE = true; private final static boolean NOT_ENOUGH_SPACE = false; private final static long VERY_LONG = 100000000; private Answer<Void> withShortPause = new Answer<Void>() { @Override public Void answer(InvocationOnMock invocation) throws Throwable { Thread.sleep(50); return null; } }; @Before public void beforeEachTest() throws Exception { when(hazelMock.getLock(anyString())).thenReturn(mock(ILock.class)); executorSutA = initSut(2); executorSutB = initSut(2); executorSutC = initSut(2); } private HazelcastExecutor initSut(int poolSize) { HazelcastExecutor executor = new HazelcastExecutor(poolSize, hazelMock); executor.setLock(lock); executor.setCondition(conditionForExecution); executor.setQueuedProcesses(queuedProcesses); executor.setRunningProcesses(runningProcesses); return executor; } @Test public void shouldExecuteOne() throws InterruptedException { configureProcessMock(processMockA1, "book1", OcrPriority.NORMAL, 1L, ENOUGH_SPACE); executorSutA.execute(processMockA1); shutdownExecutors(); verify(processMockA1).run(); } @Test public void shouldExecuteTwo() throws InterruptedException { configureProcessMock(processMockA1, "book1", OcrPriority.NORMAL, 1L, ENOUGH_SPACE); configureProcessMock(processMockA2, "book2", OcrPriority.NORMAL, 2L, ENOUGH_SPACE); executorSutA.execute(processMockA1); executorSutA.execute(processMockA2); shutdownExecutors(); verify(processMockA1).run(); verify(processMockA2).run(); } @Test public void shouldWakeUpByItself() throws InterruptedException { configureProcessMock(processMockA1, "book1", OcrPriority.NORMAL, 1L, NOT_ENOUGH_SPACE, ENOUGH_SPACE); executorSutA.setWaitingTime(10); executorSutA.execute(processMockA1); shutdownExecutors(); verify(processMockA1).run(); } @Test public void shouldNeverWakeUp() throws InterruptedException { configureProcessMock(processMockA1, "book1", OcrPriority.NORMAL, 1L, NOT_ENOUGH_SPACE); executorSutA.setWaitingTime(10); executorSutA.execute(processMockA1); shutdownExecutors(); verify(processMockA1, never()).run(); } @Test public void secondShouldFreeFirst() throws InterruptedException { configureProcessMock(processMockA1, "book1", OcrPriority.NORMAL, 1L, NOT_ENOUGH_SPACE, ENOUGH_SPACE); configureProcessMock(processMockA2, "book2", OcrPriority.ABOVENORMAL, 2L, ENOUGH_SPACE); executorSutA.setWaitingTime(VERY_LONG); executorSutA.execute(processMockA1); Thread.sleep(10); executorSutA.execute(processMockA2); shutdownExecutors(); verify(processMockA1).run(); verify(processMockA2).run(); } @Test public void shouldRunOneInEachExecutor() throws InterruptedException { configureProcessMock(processMockA1, "bookA", OcrPriority.NORMAL, 1L, ENOUGH_SPACE); configureProcessMock(processMockB1, "bookB", OcrPriority.NORMAL, 2L, ENOUGH_SPACE); executorSutA.execute(processMockA1); executorSutB.execute(processMockB1); shutdownExecutors(); verify(processMockA1).run(); verify(processMockB1).run(); } @Test public void shouldRunTwoInEachExecutor() throws InterruptedException { configureProcessMock(processMockA1, "bookA1", OcrPriority.NORMAL, 1L, ENOUGH_SPACE); configureProcessMock(processMockA2, "bookA2", OcrPriority.NORMAL, 2L, ENOUGH_SPACE); configureProcessMock(processMockB1, "bookB1", OcrPriority.NORMAL, 3L, ENOUGH_SPACE); configureProcessMock(processMockB2, "bookB2", OcrPriority.NORMAL, 4L, ENOUGH_SPACE); executorSutA.execute(processMockA1); executorSutA.execute(processMockA2); executorSutB.execute(processMockB1); executorSutB.execute(processMockB2); shutdownExecutors(); verify(processMockA1).run(); verify(processMockA2).run(); verify(processMockB1).run(); verify(processMockB2).run(); } @Test public void shouldPreferHigherPriority() throws InterruptedException { executorSutA = initSut(1); executorSutB = initSut(1); executorSutC = initSut(1); configureProcessMock(processMockA1, "bookA1", OcrPriority.NORMAL, 1L, ENOUGH_SPACE); doAnswer(withShortPause).when(processMockA1).run(); configureProcessMock(processMockB1, "bookB1", OcrPriority.NORMAL, 4L, ENOUGH_SPACE); configureProcessMock(processMockC1, "bookC1", OcrPriority.ABOVENORMAL, 5L, ENOUGH_SPACE); executorSutA.execute(processMockA1); Thread.sleep(3); executorSutB.execute(processMockB1); Thread.sleep(3); executorSutC.execute(processMockC1); shutdownExecutors(); verify(processMockA1).run(); verify(processMockB1).run(); verify(processMockC1).run(); boolean lastWasPrioritized = executorSutC.getStartedLastProcessAt() < executorSutB.getStartedLastProcessAt(); assertTrue("The last process should move up the queue", lastWasPrioritized); } @Test public void shouldPreferSmallerTimestamp() throws InterruptedException { executorSutA = initSut(1); executorSutB = initSut(1); executorSutC = initSut(1); configureProcessMock(processMockA1, "bookA1", OcrPriority.NORMAL, 1L, ENOUGH_SPACE); doAnswer(withShortPause).when(processMockA1).run(); configureProcessMock(processMockB1, "bookB1", OcrPriority.NORMAL, 4L, ENOUGH_SPACE); configureProcessMock(processMockC1, "bookC1", OcrPriority.NORMAL, 5L, ENOUGH_SPACE); executorSutA.execute(processMockA1); Thread.sleep(3); executorSutC.execute(processMockC1); executorSutB.execute(processMockB1); shutdownExecutors(); verify(processMockA1).run(); verify(processMockB1).run(); verify(processMockC1).run(); boolean orderedByTimestamp = executorSutB.getStartedLastProcessAt() < executorSutC.getStartedLastProcessAt(); assertTrue("The processes should have been ordered by timestamp", orderedByTimestamp); } private void configureProcessMock(AbbyyProcess processMock, String processId, OcrPriority prio, long startedAtMillis, boolean... hasEnoughSpace) { when(processMock.getProcessId()).thenReturn(processId); when(processMock.getPriority()).thenReturn(prio); when(processMock.getStartedAt()).thenReturn(startedAtMillis); if (hasEnoughSpace.length == 1) { when(processMock.hasEnoughSpaceForExecution()).thenReturn(hasEnoughSpace[0]); } else { when(processMock.hasEnoughSpaceForExecution()).thenReturn(hasEnoughSpace[0], hasEnoughSpace[1]); } } private void shutdownExecutors() throws InterruptedException { executorSutA.shutdown(); executorSutA.awaitTermination(100, TimeUnit.MILLISECONDS); executorSutB.shutdown(); executorSutB.awaitTermination(100, TimeUnit.MILLISECONDS); executorSutC.shutdown(); executorSutC.awaitTermination(100, TimeUnit.MILLISECONDS); } }