/* * Copyright 2014-2017 Real Logic Ltd. * * 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 io.aeron.driver; import io.aeron.driver.media.ReceiveChannelEndpoint; import org.junit.Before; import org.junit.Test; import org.mockito.InOrder; import io.aeron.logbuffer.LogBufferDescriptor; import io.aeron.protocol.DataHeaderFlyweight; import io.aeron.protocol.SetupFlyweight; import org.agrona.concurrent.UnsafeBuffer; import java.net.InetSocketAddress; import static org.mockito.Mockito.*; public class DataPacketDispatcherTest { private static final long CORRELATION_ID_1 = 101; private static final long CORRELATION_ID_2 = 102; private static final int STREAM_ID = 10; private static final int INITIAL_TERM_ID = 3; private static final int ACTIVE_TERM_ID = 3; private static final int SESSION_ID = 1; private static final int TERM_OFFSET = 0; private static final int LENGTH = DataHeaderFlyweight.HEADER_LENGTH + 100; private static final int MTU_LENGTH = 1024; private static final int TERM_LENGTH = LogBufferDescriptor.TERM_MIN_LENGTH; private static final InetSocketAddress SRC_ADDRESS = new InetSocketAddress("localhost", 4510); private final DriverConductorProxy mockConductorProxy = mock(DriverConductorProxy.class); private final Receiver mockReceiver = mock(Receiver.class); private final DataPacketDispatcher dispatcher = new DataPacketDispatcher(mockConductorProxy, mockReceiver); private final DataHeaderFlyweight mockHeader = mock(DataHeaderFlyweight.class); private final SetupFlyweight mockSetupHeader = mock(SetupFlyweight.class); private final UnsafeBuffer mockBuffer = mock(UnsafeBuffer.class); private final PublicationImage mockImage = mock(PublicationImage.class); private final ReceiveChannelEndpoint mockChannelEndpoint = mock(ReceiveChannelEndpoint.class); @Before public void setUp() throws Exception { when(mockHeader.sessionId()).thenReturn(SESSION_ID); when(mockHeader.streamId()).thenReturn(STREAM_ID); when(mockHeader.termId()).thenReturn(ACTIVE_TERM_ID); when(mockHeader.termOffset()).thenReturn(TERM_OFFSET); when(mockImage.sessionId()).thenReturn(SESSION_ID); when(mockImage.streamId()).thenReturn(STREAM_ID); when(mockSetupHeader.sessionId()).thenReturn(SESSION_ID); when(mockSetupHeader.streamId()).thenReturn(STREAM_ID); when(mockSetupHeader.activeTermId()).thenReturn(ACTIVE_TERM_ID); when(mockSetupHeader.initialTermId()).thenReturn(INITIAL_TERM_ID); when(mockSetupHeader.termOffset()).thenReturn(TERM_OFFSET); when(mockSetupHeader.mtuLength()).thenReturn(MTU_LENGTH); when(mockSetupHeader.termLength()).thenReturn(TERM_LENGTH); } @Test public void shouldElicitSetupMessageWhenDataArrivesForSubscriptionWithoutImage() { dispatcher.addSubscription(STREAM_ID); dispatcher.onDataPacket(mockChannelEndpoint, mockHeader, mockBuffer, LENGTH, SRC_ADDRESS); verify(mockImage, never()).insertPacket(anyInt(), anyInt(), any(), anyInt()); verify(mockChannelEndpoint).sendSetupElicitingStatusMessage(SRC_ADDRESS, SESSION_ID, STREAM_ID); verify(mockReceiver).addPendingSetupMessage(SESSION_ID, STREAM_ID, mockChannelEndpoint, false, SRC_ADDRESS); } @Test public void shouldOnlyElicitSetupMessageOnceWhenDataArrivesForSubscriptionWithoutImage() { dispatcher.addSubscription(STREAM_ID); dispatcher.onDataPacket(mockChannelEndpoint, mockHeader, mockBuffer, LENGTH, SRC_ADDRESS); dispatcher.onDataPacket(mockChannelEndpoint, mockHeader, mockBuffer, LENGTH, SRC_ADDRESS); dispatcher.onDataPacket(mockChannelEndpoint, mockHeader, mockBuffer, LENGTH, SRC_ADDRESS); verify(mockImage, never()).insertPacket(anyInt(), anyInt(), any(), anyInt()); verify(mockChannelEndpoint).sendSetupElicitingStatusMessage(SRC_ADDRESS, SESSION_ID, STREAM_ID); verify(mockReceiver).addPendingSetupMessage(SESSION_ID, STREAM_ID, mockChannelEndpoint, false, SRC_ADDRESS); } @Test public void shouldElicitSetupMessageAgainWhenDataArrivesForSubscriptionWithoutImageAfterRemovePendingSetup() { dispatcher.addSubscription(STREAM_ID); dispatcher.onDataPacket(mockChannelEndpoint, mockHeader, mockBuffer, LENGTH, SRC_ADDRESS); dispatcher.onDataPacket(mockChannelEndpoint, mockHeader, mockBuffer, LENGTH, SRC_ADDRESS); dispatcher.removePendingSetup(SESSION_ID, STREAM_ID); dispatcher.onDataPacket(mockChannelEndpoint, mockHeader, mockBuffer, LENGTH, SRC_ADDRESS); dispatcher.onDataPacket(mockChannelEndpoint, mockHeader, mockBuffer, LENGTH, SRC_ADDRESS); verify(mockImage, never()).insertPacket(anyInt(), anyInt(), any(), anyInt()); verify(mockChannelEndpoint, times(2)).sendSetupElicitingStatusMessage(SRC_ADDRESS, SESSION_ID, STREAM_ID); verify(mockReceiver, times(2)) .addPendingSetupMessage(SESSION_ID, STREAM_ID, mockChannelEndpoint, false, SRC_ADDRESS); } @Test public void shouldRequestCreateImageUponReceivingSetup() { dispatcher.addSubscription(STREAM_ID); dispatcher.onSetupMessage(mockChannelEndpoint, mockSetupHeader, mockBuffer, SRC_ADDRESS); verify(mockConductorProxy).createPublicationImage( SESSION_ID, STREAM_ID, INITIAL_TERM_ID, ACTIVE_TERM_ID, TERM_OFFSET, TERM_LENGTH, MTU_LENGTH, SRC_ADDRESS, SRC_ADDRESS, mockChannelEndpoint); } @Test public void shouldOnlyRequestCreateImageOnceUponReceivingSetup() { dispatcher.addSubscription(STREAM_ID); dispatcher.onSetupMessage(mockChannelEndpoint, mockSetupHeader, mockBuffer, SRC_ADDRESS); dispatcher.onSetupMessage(mockChannelEndpoint, mockSetupHeader, mockBuffer, SRC_ADDRESS); dispatcher.onSetupMessage(mockChannelEndpoint, mockSetupHeader, mockBuffer, SRC_ADDRESS); verify(mockConductorProxy).createPublicationImage( SESSION_ID, STREAM_ID, INITIAL_TERM_ID, ACTIVE_TERM_ID, TERM_OFFSET, TERM_LENGTH, MTU_LENGTH, SRC_ADDRESS, SRC_ADDRESS, mockChannelEndpoint); } @Test public void shouldNotRequestCreateImageOnceUponReceivingSetupAfterImageAdded() { dispatcher.addSubscription(STREAM_ID); dispatcher.addPublicationImage(mockImage); dispatcher.onSetupMessage(mockChannelEndpoint, mockSetupHeader, mockBuffer, SRC_ADDRESS); verifyZeroInteractions(mockConductorProxy); } @Test public void shouldSetImageInactiveOnRemoveSubscription() { dispatcher.addSubscription(STREAM_ID); dispatcher.addPublicationImage(mockImage); dispatcher.removeSubscription(STREAM_ID); verify(mockImage).ifActiveGoInactive(); } @Test public void shouldSetImageInactiveOnRemoveImage() { dispatcher.addSubscription(STREAM_ID); dispatcher.addPublicationImage(mockImage); dispatcher.removePublicationImage(mockImage); verify(mockImage).ifActiveGoInactive(); } @Test public void shouldIgnoreDataAndSetupAfterImageRemoved() { dispatcher.addSubscription(STREAM_ID); dispatcher.addPublicationImage(mockImage); dispatcher.removePublicationImage(mockImage); dispatcher.onDataPacket(mockChannelEndpoint, mockHeader, mockBuffer, LENGTH, SRC_ADDRESS); dispatcher.onSetupMessage(mockChannelEndpoint, mockSetupHeader, mockBuffer, SRC_ADDRESS); verifyZeroInteractions(mockConductorProxy); verifyZeroInteractions(mockReceiver); } @Test public void shouldNotIgnoreDataAndSetupAfterImageRemovedAndCooldownRemoved() { dispatcher.addSubscription(STREAM_ID); dispatcher.addPublicationImage(mockImage); dispatcher.removePublicationImage(mockImage); dispatcher.removeCoolDown(SESSION_ID, STREAM_ID); dispatcher.onDataPacket(mockChannelEndpoint, mockHeader, mockBuffer, LENGTH, SRC_ADDRESS); dispatcher.onSetupMessage(mockChannelEndpoint, mockSetupHeader, mockBuffer, SRC_ADDRESS); verify(mockImage, never()).insertPacket(anyInt(), anyInt(), any(), anyInt()); final InOrder inOrder = inOrder(mockChannelEndpoint, mockReceiver, mockConductorProxy); inOrder.verify(mockChannelEndpoint).sendSetupElicitingStatusMessage(SRC_ADDRESS, SESSION_ID, STREAM_ID); inOrder.verify(mockReceiver) .addPendingSetupMessage(SESSION_ID, STREAM_ID, mockChannelEndpoint, false, SRC_ADDRESS); inOrder.verify(mockConductorProxy).createPublicationImage( SESSION_ID, STREAM_ID, INITIAL_TERM_ID, ACTIVE_TERM_ID, TERM_OFFSET, TERM_LENGTH, MTU_LENGTH, SRC_ADDRESS, SRC_ADDRESS, mockChannelEndpoint); } @Test public void shouldDispatchDataToCorrectImage() { dispatcher.addSubscription(STREAM_ID); dispatcher.addPublicationImage(mockImage); dispatcher.onDataPacket(mockChannelEndpoint, mockHeader, mockBuffer, LENGTH, SRC_ADDRESS); verify(mockImage).status(PublicationImage.Status.ACTIVE); verify(mockImage).insertPacket(ACTIVE_TERM_ID, TERM_OFFSET, mockBuffer, LENGTH); } @Test public void shouldNotRemoveNewPublicationImageFromOldRemovePublicationImageAfterRemoveSubscription() { final PublicationImage mockImage1 = mock(PublicationImage.class); final PublicationImage mockImage2 = mock(PublicationImage.class); when(mockImage1.sessionId()).thenReturn(SESSION_ID); when(mockImage1.streamId()).thenReturn(STREAM_ID); when(mockImage1.correlationId()).thenReturn(CORRELATION_ID_1); when(mockImage2.sessionId()).thenReturn(SESSION_ID); when(mockImage2.streamId()).thenReturn(STREAM_ID); when(mockImage2.correlationId()).thenReturn(CORRELATION_ID_2); dispatcher.addSubscription(STREAM_ID); dispatcher.addPublicationImage(mockImage1); dispatcher.removeSubscription(STREAM_ID); dispatcher.addSubscription(STREAM_ID); dispatcher.addPublicationImage(mockImage2); dispatcher.removePublicationImage(mockImage1); dispatcher.onDataPacket(mockChannelEndpoint, mockHeader, mockBuffer, LENGTH, SRC_ADDRESS); verify(mockImage1, never()).insertPacket(anyInt(), anyInt(), any(), anyInt()); verify(mockImage2).insertPacket(ACTIVE_TERM_ID, TERM_OFFSET, mockBuffer, LENGTH); } }