package com.rackspacecloud.blueflood.service; import com.rackspacecloud.blueflood.rollup.Granularity; import com.rackspacecloud.blueflood.rollup.SlotKey; import org.junit.Before; import org.junit.Test; import org.mockito.Matchers; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.concurrent.BlockingQueue; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ThreadPoolExecutor; import static org.junit.Assert.*; import static org.mockito.Matchers.anyLong; import static org.mockito.Mockito.*; public class RollupServiceTest { ScheduleContext context; ShardStateManager shardStateManager; ThreadPoolExecutor locatorFetchExecutors; ThreadPoolExecutor rollupReadExecutors; ThreadPoolExecutor rollupWriteExecutors; long rollupDelayMillis; long delayedMetricRollupDelayMillis; long rollupWaitPeriodMillis; long pollerPeriod; long configRefreshInterval; RollupService service; @Before public void setUp() { context = mock(ScheduleContext.class); shardStateManager = mock(ShardStateManager.class); locatorFetchExecutors = mock(ThreadPoolExecutor.class); rollupReadExecutors = mock(ThreadPoolExecutor.class); rollupWriteExecutors = mock(ThreadPoolExecutor.class); rollupDelayMillis = 300000; delayedMetricRollupDelayMillis = 300000; rollupWaitPeriodMillis = 300000; pollerPeriod = 0; configRefreshInterval = 10000; service = new RollupService(context, shardStateManager, locatorFetchExecutors, rollupReadExecutors, rollupWriteExecutors, rollupDelayMillis, delayedMetricRollupDelayMillis, rollupWaitPeriodMillis, pollerPeriod, configRefreshInterval); } @Test public void pollSchedulesEligibleSlots() { // when service.poll(); // then verify(context).scheduleEligibleSlots(anyLong(), anyLong(), anyLong()); verifyNoMoreInteractions(context); verifyZeroInteractions(shardStateManager); verifyZeroInteractions(locatorFetchExecutors); verifyZeroInteractions(rollupReadExecutors); verifyZeroInteractions(rollupWriteExecutors); } @Test public void runSingleSlotDequeuesAndExecutes() { // given when(context.hasScheduled()).thenReturn(true).thenReturn(false); SlotKey slotkey = SlotKey.of(Granularity.MIN_20, 2, 0); when(shardStateManager.getUpdateStamp(slotkey)).thenReturn(mock(UpdateStamp.class)); doReturn(slotkey).when(context).getNextScheduled(); final Runnable[] capture = new Runnable[1]; doAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocationOnMock) throws Throwable { capture[0] = (Runnable)invocationOnMock.getArguments()[0]; // now that the runnable has been queued, stop the outer loop service.setShouldKeepRunning(false); return null; } }).when(locatorFetchExecutors).execute(Matchers.<Runnable>any()); // when service.run(); // then verify(context).scheduleEligibleSlots(anyLong(), anyLong(), anyLong()); // from poll verify(context, times(2)).hasScheduled(); verify(context).getNextScheduled(); verify(context, times(2)).getCurrentTimeMillis(); // one from LocatorFetchRunnable ctor, one from run verify(context, times(1)).isReroll(any(SlotKey.class)); verifyNoMoreInteractions(context); verify(locatorFetchExecutors).execute(Matchers.<Runnable>any()); assertSame(LocatorFetchRunnable.class, capture[0].getClass()); verifyNoMoreInteractions(locatorFetchExecutors); verifyZeroInteractions(rollupReadExecutors); verifyZeroInteractions(rollupWriteExecutors); } @Test public void ifTheExecutionIsRejectedThenTheSlotKeyIsPushedBack() { // given when(context.hasScheduled()).thenReturn(true).thenReturn(false); SlotKey slotkey = SlotKey.of(Granularity.MIN_20, 2, 0); when(shardStateManager.getUpdateStamp(slotkey)).thenReturn(mock(UpdateStamp.class)); doReturn(slotkey).when(context).getNextScheduled(); final RejectedExecutionException cause = new RejectedExecutionException("exception for testing purposes"); doAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocationOnMock) throws Throwable { // now we're inside the loop, stop the outer loop service.setShouldKeepRunning(false); throw cause; } }).when(locatorFetchExecutors).execute(Matchers.<Runnable>any()); // when service.run(); // then verify(context).scheduleEligibleSlots(anyLong(), anyLong(), anyLong()); // from poll verify(context, times(2)).hasScheduled(); verify(context).getNextScheduled(); verify(context, times(2)).getCurrentTimeMillis(); // one from LocatorFetchRunnable ctor, one from run verify(context).pushBackToScheduled(Matchers.<SlotKey>any(), anyBoolean()); verify(context, times(1)).isReroll(any(SlotKey.class)); verifyNoMoreInteractions(context); verify(locatorFetchExecutors).execute(Matchers.<Runnable>any()); verifyNoMoreInteractions(locatorFetchExecutors); verifyZeroInteractions(rollupReadExecutors); verifyZeroInteractions(rollupWriteExecutors); } @Test public void setServerTimeSetsContextTime() { //when service.setServerTime(1234L); // then verify(context).setCurrentTimeMillis(anyLong()); verifyNoMoreInteractions(context); verifyZeroInteractions(shardStateManager); verifyZeroInteractions(locatorFetchExecutors); verifyZeroInteractions(rollupReadExecutors); verifyZeroInteractions(rollupWriteExecutors); } @Test public void getServerTimeGetsContextTime() { // given long expected = 1234L; doReturn(expected).when(context).getCurrentTimeMillis(); // when long actual = service.getServerTime(); // then assertEquals(expected, actual); verify(context).getCurrentTimeMillis(); verifyNoMoreInteractions(context); verifyZeroInteractions(shardStateManager); verifyZeroInteractions(locatorFetchExecutors); verifyZeroInteractions(rollupReadExecutors); verifyZeroInteractions(rollupWriteExecutors); } @Test public void getKeepingServerTimeGetsKeepingServerTime() { // expect assertEquals(true, service.getKeepingServerTime()); } @Test public void setKeepingServerTimeSetsKeepingServerTime() { // precondition assertEquals(true, service.getKeepingServerTime()); // when service.setKeepingServerTime(false); // then assertEquals(false, service.getKeepingServerTime()); } @Test public void getPollerPeriodGetsPollerPeriod() { // expect assertEquals(0, service.getPollerPeriod()); } @Test public void setPollerPeriodSetsPollerPeriod() { // precondition assertEquals(0, service.getPollerPeriod()); // when service.setPollerPeriod(1234L); // then assertEquals(1234L, service.getPollerPeriod()); } @Test public void getScheduledSlotCheckCountGetsCount() { // given int expected = 3; doReturn(expected).when(context).getScheduledCount(); // when int actual = service.getScheduledSlotCheckCount(); // then assertEquals(expected, actual); verify(context).getScheduledCount(); verifyNoMoreInteractions(context); } @Test public void testGetSlotCheckConcurrency() { // given int expected = 12; doReturn(expected).when(locatorFetchExecutors).getMaximumPoolSize(); // when int actual = service.getSlotCheckConcurrency(); // then assertEquals(expected, actual); verify(locatorFetchExecutors).getMaximumPoolSize(); verifyNoMoreInteractions(locatorFetchExecutors); } @Test public void testSetSlotCheckConcurrency() { // when service.setSlotCheckConcurrency(3); // then verify(locatorFetchExecutors).setCorePoolSize(anyInt()); verify(locatorFetchExecutors).setMaximumPoolSize(anyInt()); verifyNoMoreInteractions(locatorFetchExecutors); } @Test public void testGetRollupConcurrency() { // given int expected = 12; doReturn(expected).when(rollupReadExecutors).getMaximumPoolSize(); // when int actual = service.getRollupConcurrency(); // then assertEquals(expected, actual); verify(rollupReadExecutors).getMaximumPoolSize(); verifyNoMoreInteractions(rollupReadExecutors); } @Test public void testSetRollupConcurrency() { // when service.setRollupConcurrency(3); // then verify(rollupReadExecutors).setCorePoolSize(anyInt()); verify(rollupReadExecutors).setMaximumPoolSize(anyInt()); verifyNoMoreInteractions(rollupReadExecutors); } @Test public void getQueuedRollupCountReturnsQueueSize() { //given BlockingQueue<Runnable> queue = mock(BlockingQueue.class); int expected1 = 123; int expected2 = 45; when(queue.size()).thenReturn(expected1).thenReturn(expected2); when(rollupReadExecutors.getQueue()).thenReturn(queue); // when int count = service.getQueuedRollupCount(); // then assertEquals(expected1, count); // when count = service.getQueuedRollupCount(); // then assertEquals(expected2, count); } @Test public void testGetInFlightRollupCount() { //given int expected1 = 123; int expected2 = 45; when(rollupReadExecutors.getActiveCount()) .thenReturn(expected1) .thenReturn(expected2); // when int count = service.getInFlightRollupCount(); // then assertEquals(expected1, count); // when count = service.getInFlightRollupCount(); // then assertEquals(expected2, count); } @Test public void getActiveGetsActiveFlag() { // expect assertEquals(true, service.getActive()); } @Test public void setActiveSetsActiveFlag() { // precondition assertEquals(true, service.getActive()); // when service.setActive(false); // then assertEquals(false, service.getActive()); } @Test public void addShardDoesNotAddShardsAlreadyManaged() { // given HashSet<Integer> managedShards = new HashSet<Integer>(); managedShards.add(0); doReturn(managedShards).when(shardStateManager).getManagedShards(); // when service.addShard(0); // then verifyZeroInteractions(context); } @Test public void addShardAddsShardsNotYetManaged() { // given HashSet<Integer> managedShards = new HashSet<Integer>(); doReturn(managedShards).when(shardStateManager).getManagedShards(); // when service.addShard(0); // then verify(context).addShard(anyInt()); verifyNoMoreInteractions(context); } @Test public void removeShardRemovesManagedShards() { // given HashSet<Integer> managedShards = new HashSet<Integer>(); managedShards.add(0); doReturn(managedShards).when(shardStateManager).getManagedShards(); // when service.removeShard(0); // then verify(context).removeShard(anyInt()); verifyNoMoreInteractions(context); } @Test public void removeShardDoesNotRemovesShardsNotManaged() { // given HashSet<Integer> managedShards = new HashSet<Integer>(); doReturn(managedShards).when(shardStateManager).getManagedShards(); // when service.removeShard(0); // then verifyZeroInteractions(context); } @Test public void getManagedShardsGetsCollectionFromManager() { // given HashSet<Integer> managedShards = new HashSet<Integer>(); managedShards.add(0); managedShards.add(1); managedShards.add(2); doReturn(managedShards).when(shardStateManager).getManagedShards(); // when Collection<Integer> actual = service.getManagedShards(); // then assertEquals(managedShards.size(), actual.size()); assertTrue(actual.containsAll(managedShards)); verify(shardStateManager).getManagedShards(); verifyNoMoreInteractions(shardStateManager); } @Test public void getRecentlyScheduledShardsGetsFromContext() { // when Collection<Integer> recent = service.getRecentlyScheduledShards(); // then assertNotNull(recent); verify(context).getRecentlyScheduledShards(); verifyNoMoreInteractions(context); verifyZeroInteractions(shardStateManager); } @Test public void getOldestWithNullStampsReturnsEmptyCollection() { // given doReturn(null).when(context).getSlotStamps(Matchers.<Granularity>any(), anyInt()); // when Collection<String> result = service.getOldestUnrolledSlotPerGranularity(0); // then assertNotNull(result); assertEquals(0, result.size()); } @Test public void getOldestWithEmptyStampsReturnsEmptyCollection() { // given HashMap<Integer, UpdateStamp> empty = new HashMap<Integer, UpdateStamp>(); doReturn(empty).when(context).getSlotStamps(Matchers.<Granularity>any(), anyInt()); // when Collection<String> result = service.getOldestUnrolledSlotPerGranularity(0); // then assertNotNull(result); assertEquals(0, result.size()); } @Test public void getOldestWithSingleStampReturnsSame() { // given HashMap<Integer, UpdateStamp> stamps = new HashMap<Integer, UpdateStamp>(); long time = 1234L; UpdateStamp stamp = new UpdateStamp(time, UpdateStamp.State.Active, false); stamps.put(0, stamp); when(context.getSlotStamps(Matchers.<Granularity>any(), anyInt())) .thenReturn(stamps) .thenReturn(null); SlotState slotState = new SlotState(Granularity.MIN_5, 0, stamp.getState()) .withTimestamp(time); String expected = slotState.toString(); // when Collection<String> result = service.getOldestUnrolledSlotPerGranularity(0); // then assertNotNull(result); assertEquals(1, result.size()); assertTrue(result.contains(expected)); } @Test public void getOldestWithTimeInFutureReturnsEmpty() { // given HashMap<Integer, UpdateStamp> stamps = new HashMap<Integer, UpdateStamp>(); long time = System.currentTimeMillis() + 1234L; UpdateStamp stamp = new UpdateStamp(time, UpdateStamp.State.Active, false); stamps.put(0, stamp); when(context.getSlotStamps(Matchers.<Granularity>any(), anyInt())) .thenReturn(stamps) .thenReturn(null); // when Collection<String> result = service.getOldestUnrolledSlotPerGranularity(0); // then assertNotNull(result); assertEquals(0, result.size()); } @Test public void getOldestWithTwoStampsTimeInFutureReturnsOlderOfTheTwo() { // given HashMap<Integer, UpdateStamp> stamps = new HashMap<Integer, UpdateStamp>(); long time1 = 1234L; UpdateStamp stamp1 = new UpdateStamp(time1, UpdateStamp.State.Active, false); stamps.put(0, stamp1); long time2 = 1233L; UpdateStamp stamp2 = new UpdateStamp(time2, UpdateStamp.State.Active, false); stamps.put(1, stamp2); when(context.getSlotStamps(Matchers.<Granularity>any(), anyInt())) .thenReturn(stamps) .thenReturn(null); SlotState slotState = new SlotState(Granularity.MIN_5, 1, stamp2.getState()) .withTimestamp(time2); String expected = slotState.toString(); // when Collection<String> result = service.getOldestUnrolledSlotPerGranularity(0); // then assertNotNull(result); assertEquals(1, result.size()); assertTrue(result.contains(expected)); } @Test public void getOldestSkipsRolledStamps() { // given HashMap<Integer, UpdateStamp> stamps = new HashMap<Integer, UpdateStamp>(); long time1 = 1234L; UpdateStamp stamp1 = new UpdateStamp(time1, UpdateStamp.State.Active, false); stamps.put(0, stamp1); long time2 = 1233L; UpdateStamp stamp2 = new UpdateStamp(time2, UpdateStamp.State.Rolled, false); stamps.put(1, stamp2); when(context.getSlotStamps(Matchers.<Granularity>any(), anyInt())) .thenReturn(stamps) .thenReturn(null); SlotState slotState = new SlotState(Granularity.MIN_5, 0, stamp1.getState()) .withTimestamp(time1); String expected = slotState.toString(); // when Collection<String> result = service.getOldestUnrolledSlotPerGranularity(0); // then assertNotNull(result); assertEquals(1, result.size()); assertTrue(result.contains(expected)); } }