package io.aeron.logbuffer; import org.junit.Before; import org.junit.Test; import org.mockito.InOrder; import io.aeron.protocol.DataHeaderFlyweight; import org.agrona.concurrent.UnsafeBuffer; import static java.lang.Integer.valueOf; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.*; import static io.aeron.logbuffer.FrameDescriptor.*; import static io.aeron.protocol.HeaderFlyweight.HDR_TYPE_DATA; import static org.agrona.BitUtil.align; public class TermScannerTest { private static final int TERM_BUFFER_CAPACITY = LogBufferDescriptor.TERM_MIN_LENGTH; private static final int MTU_LENGTH = 1024; private static final int HEADER_LENGTH = DataHeaderFlyweight.HEADER_LENGTH; private final UnsafeBuffer termBuffer = mock(UnsafeBuffer.class); @Before public void setUp() { when(termBuffer.capacity()).thenReturn(TERM_BUFFER_CAPACITY); } @Test public void shouldPackPaddingAndOffsetIntoResultingStatus() { final int padding = 77; final int available = 65000; final long scanOutcome = TermScanner.pack(padding, available); assertThat(TermScanner.padding(scanOutcome), is(padding)); assertThat(TermScanner.available(scanOutcome), is(available)); } @Test public void shouldReturnZeroOnEmptyLog() { final long scanOutcome = TermScanner.scanForAvailability(termBuffer, 0, MTU_LENGTH); assertThat(TermScanner.available(scanOutcome), is(0)); assertThat(TermScanner.padding(scanOutcome), is(0)); } @Test public void shouldScanSingleMessage() { final int msgLength = 1; final int frameLength = HEADER_LENGTH + msgLength; final int alignedFrameLength = align(frameLength, FRAME_ALIGNMENT); final int frameOffset = 0; when(termBuffer.getIntVolatile(frameOffset)).thenReturn(frameLength); when(termBuffer.getShort(typeOffset(frameOffset))).thenReturn((short)HDR_TYPE_DATA); final long scanOutcome = TermScanner.scanForAvailability(termBuffer, frameOffset, MTU_LENGTH); assertThat(TermScanner.available(scanOutcome), is(alignedFrameLength)); assertThat(TermScanner.padding(scanOutcome), is(0)); final InOrder inOrder = inOrder(termBuffer); inOrder.verify(termBuffer).getIntVolatile(frameOffset); inOrder.verify(termBuffer).getShort(typeOffset(frameOffset)); } @Test public void shouldFailToScanMessageLargerThanMaxLength() { final int msgLength = 1; final int frameLength = HEADER_LENGTH + msgLength; final int alignedFrameLength = align(frameLength, FRAME_ALIGNMENT); final int maxLength = alignedFrameLength - 1; final int frameOffset = 0; when(termBuffer.getIntVolatile(frameOffset)).thenReturn(frameLength); when(termBuffer.getShort(typeOffset(frameOffset))).thenReturn((short)HDR_TYPE_DATA); final long scanOutcome = TermScanner.scanForAvailability(termBuffer, frameOffset, maxLength); assertThat(TermScanner.available(scanOutcome), is(0)); assertThat(TermScanner.padding(scanOutcome), is(0)); final InOrder inOrder = inOrder(termBuffer); inOrder.verify(termBuffer).getIntVolatile(frameOffset); inOrder.verify(termBuffer).getShort(typeOffset(frameOffset)); } @Test public void shouldScanTwoMessagesThatFitInSingleMtu() { final int msgLength = 100; final int frameLength = HEADER_LENGTH + msgLength; final int alignedFrameLength = align(frameLength, FRAME_ALIGNMENT); int frameOffset = 0; when(termBuffer.getIntVolatile(frameOffset)).thenReturn(frameLength); when(termBuffer.getShort(typeOffset(frameOffset))).thenReturn((short)HDR_TYPE_DATA); when(termBuffer.getIntVolatile(frameOffset + alignedFrameLength)).thenReturn(alignedFrameLength); when(termBuffer.getShort(typeOffset(frameOffset + alignedFrameLength))).thenReturn((short)HDR_TYPE_DATA); final long scanOutcome = TermScanner.scanForAvailability(termBuffer, frameOffset, MTU_LENGTH); assertThat(TermScanner.available(scanOutcome), is(alignedFrameLength * 2)); assertThat(TermScanner.padding(scanOutcome), is(0)); final InOrder inOrder = inOrder(termBuffer); inOrder.verify(termBuffer).getIntVolatile(frameOffset); inOrder.verify(termBuffer).getShort(typeOffset(frameOffset)); frameOffset += alignedFrameLength; inOrder.verify(termBuffer).getIntVolatile(frameOffset); inOrder.verify(termBuffer).getShort(typeOffset(frameOffset)); } @Test public void shouldScanTwoMessagesAndStopAtMtuBoundary() { final int frameTwoLength = align(HEADER_LENGTH + 1, FRAME_ALIGNMENT); final int frameOneLength = MTU_LENGTH - frameTwoLength; int frameOffset = 0; when(termBuffer.getIntVolatile(frameOffset)).thenReturn(frameOneLength); when(termBuffer.getShort(typeOffset(frameOffset))).thenReturn((short)HDR_TYPE_DATA); when(termBuffer.getIntVolatile(frameOffset + frameOneLength)).thenReturn(frameTwoLength); when(termBuffer.getShort(typeOffset(frameOffset + frameOneLength))).thenReturn((short)HDR_TYPE_DATA); final long scanOutcome = TermScanner.scanForAvailability(termBuffer, frameOffset, MTU_LENGTH); assertThat(TermScanner.available(scanOutcome), is(frameOneLength + frameTwoLength)); assertThat(TermScanner.padding(scanOutcome), is(0)); final InOrder inOrder = inOrder(termBuffer); inOrder.verify(termBuffer).getIntVolatile(frameOffset); inOrder.verify(termBuffer).getShort(typeOffset(frameOffset)); frameOffset += frameOneLength; inOrder.verify(termBuffer).getIntVolatile(frameOffset); inOrder.verify(termBuffer).getShort(typeOffset(frameOffset)); } @Test public void shouldScanTwoMessagesAndStopAtSecondThatSpansMtu() { final int frameTwoLength = align(HEADER_LENGTH * 2, FRAME_ALIGNMENT); final int frameOneLength = MTU_LENGTH - (frameTwoLength / 2); int frameOffset = 0; when(termBuffer.getIntVolatile(frameOffset)).thenReturn(frameOneLength); when(termBuffer.getShort(typeOffset(frameOffset))).thenReturn((short)HDR_TYPE_DATA); when(termBuffer.getIntVolatile(frameOffset + frameOneLength)).thenReturn(frameTwoLength); when(termBuffer.getShort(typeOffset(frameOffset + frameOneLength))).thenReturn((short)HDR_TYPE_DATA); final long scanOutcome = TermScanner.scanForAvailability(termBuffer, frameOffset, MTU_LENGTH); assertThat(TermScanner.available(scanOutcome), is(frameOneLength)); assertThat(TermScanner.padding(scanOutcome), is(0)); final InOrder inOrder = inOrder(termBuffer); inOrder.verify(termBuffer).getIntVolatile(frameOffset); inOrder.verify(termBuffer).getShort(typeOffset(frameOffset)); frameOffset += frameOneLength; inOrder.verify(termBuffer).getIntVolatile(frameOffset); inOrder.verify(termBuffer).getShort(typeOffset(frameOffset)); } @Test public void shouldScanLastFrameInBuffer() { final int alignedFrameLength = align(HEADER_LENGTH * 2, FRAME_ALIGNMENT); final int frameOffset = TERM_BUFFER_CAPACITY - alignedFrameLength; when(termBuffer.getIntVolatile(frameOffset)).thenReturn(alignedFrameLength); when(termBuffer.getShort(typeOffset(frameOffset))).thenReturn((short)HDR_TYPE_DATA); final long scanOutcome = TermScanner.scanForAvailability(termBuffer, frameOffset, MTU_LENGTH); assertThat(TermScanner.available(scanOutcome), is(alignedFrameLength)); assertThat(TermScanner.padding(scanOutcome), is(0)); } @Test public void shouldScanLastMessageInBufferPlusPadding() { final int alignedFrameLength = align(HEADER_LENGTH * 2, FRAME_ALIGNMENT); final int paddingFrameLength = align(HEADER_LENGTH * 3, FRAME_ALIGNMENT); final int frameOffset = TERM_BUFFER_CAPACITY - (alignedFrameLength + paddingFrameLength); when(valueOf(termBuffer.getIntVolatile(frameOffset))).thenReturn(alignedFrameLength); when(termBuffer.getShort(typeOffset(frameOffset))).thenReturn((short)HDR_TYPE_DATA); when(termBuffer.getIntVolatile(frameOffset + alignedFrameLength)).thenReturn(paddingFrameLength); when(termBuffer.getShort(typeOffset(frameOffset + alignedFrameLength))).thenReturn((short)PADDING_FRAME_TYPE); final long scanOutcome = TermScanner.scanForAvailability(termBuffer, frameOffset, MTU_LENGTH); assertThat(TermScanner.available(scanOutcome), is(alignedFrameLength + HEADER_LENGTH)); assertThat(TermScanner.padding(scanOutcome), is(paddingFrameLength - HEADER_LENGTH)); } @Test public void shouldScanLastMessageInBufferMinusPaddingLimitedByMtu() { final int alignedFrameLength = align(HEADER_LENGTH, FRAME_ALIGNMENT); final int frameOffset = TERM_BUFFER_CAPACITY - align(HEADER_LENGTH * 3, FRAME_ALIGNMENT); final int mtu = alignedFrameLength + 8; when(valueOf(termBuffer.getIntVolatile(frameOffset))).thenReturn(alignedFrameLength); when(termBuffer.getShort(typeOffset(frameOffset))).thenReturn((short)HDR_TYPE_DATA); when(termBuffer.getIntVolatile(frameOffset + alignedFrameLength)).thenReturn(alignedFrameLength * 2); when(termBuffer.getShort(typeOffset(frameOffset + alignedFrameLength))).thenReturn((short)PADDING_FRAME_TYPE); final long scanOutcome = TermScanner.scanForAvailability(termBuffer, frameOffset, mtu); assertThat(TermScanner.available(scanOutcome), is(alignedFrameLength)); assertThat(TermScanner.padding(scanOutcome), is(0)); } }