/* * 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 org.agrona.concurrent.broadcast; import org.junit.Before; import org.junit.Test; import org.mockito.InOrder; import org.agrona.concurrent.UnsafeBuffer; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.*; import static org.agrona.BitUtil.align; import static org.agrona.concurrent.broadcast.RecordDescriptor.*; public class BroadcastTransmitterTest { private static final int MSG_TYPE_ID = 7; private static final int CAPACITY = 1024; private static final int TOTAL_BUFFER_LENGTH = CAPACITY + BroadcastBufferDescriptor.TRAILER_LENGTH; private static final int TAIL_INTENT_COUNTER_OFFSET = CAPACITY + BroadcastBufferDescriptor.TAIL_INTENT_COUNTER_OFFSET; private static final int TAIL_COUNTER_INDEX = CAPACITY + BroadcastBufferDescriptor.TAIL_COUNTER_OFFSET; private static final int LATEST_COUNTER_INDEX = CAPACITY + BroadcastBufferDescriptor.LATEST_COUNTER_OFFSET; private final UnsafeBuffer buffer = mock(UnsafeBuffer.class); private BroadcastTransmitter broadcastTransmitter; @Before public void setUp() { when(buffer.capacity()).thenReturn(TOTAL_BUFFER_LENGTH); broadcastTransmitter = new BroadcastTransmitter(buffer); } @Test public void shouldCalculateCapacityForBuffer() { assertThat(broadcastTransmitter.capacity(), is(CAPACITY)); } @Test(expected = IllegalStateException.class) public void shouldThrowExceptionForCapacityThatIsNotPowerOfTwo() { final int capacity = 777; final int totalBufferLength = capacity + BroadcastBufferDescriptor.TRAILER_LENGTH; when(buffer.capacity()).thenReturn(totalBufferLength); new BroadcastTransmitter(buffer); } @Test(expected = IllegalArgumentException.class) public void shouldThrowExceptionWhenMaxMessageLengthExceeded() { final UnsafeBuffer srcBuffer = new UnsafeBuffer(new byte[1024]); broadcastTransmitter.transmit(MSG_TYPE_ID, srcBuffer, 0, broadcastTransmitter.maxMsgLength() + 1); } @Test(expected = IllegalArgumentException.class) public void shouldThrowExceptionWhenMessageTypeIdInvalid() { final int invalidMsgId = -1; final UnsafeBuffer srcBuffer = new UnsafeBuffer(new byte[1024]); broadcastTransmitter.transmit(invalidMsgId, srcBuffer, 0, 32); } @Test public void shouldTransmitIntoEmptyBuffer() { final long tail = 0L; final int recordOffset = (int)tail; final int length = 8; final int recordLength = length + HEADER_LENGTH; final int recordLengthAligned = align(recordLength, RECORD_ALIGNMENT); final UnsafeBuffer srcBuffer = new UnsafeBuffer(new byte[1024]); final int srcIndex = 0; broadcastTransmitter.transmit(MSG_TYPE_ID, srcBuffer, srcIndex, length); final InOrder inOrder = inOrder(buffer); inOrder.verify(buffer).getLong(TAIL_COUNTER_INDEX); inOrder.verify(buffer).putLongOrdered(TAIL_INTENT_COUNTER_OFFSET, tail + recordLengthAligned); inOrder.verify(buffer).putInt(lengthOffset(recordOffset), recordLength); inOrder.verify(buffer).putInt(typeOffset(recordOffset), MSG_TYPE_ID); inOrder.verify(buffer).putBytes(msgOffset(recordOffset), srcBuffer, srcIndex, length); inOrder.verify(buffer).putLong(LATEST_COUNTER_INDEX, tail); inOrder.verify(buffer).putLongOrdered(TAIL_COUNTER_INDEX, tail + recordLengthAligned); } @Test public void shouldTransmitIntoUsedBuffer() { final long tail = RECORD_ALIGNMENT * 3; final int recordOffset = (int)tail; final int length = 8; final int recordLength = length + HEADER_LENGTH; final int recordLengthAligned = align(recordLength, RECORD_ALIGNMENT); when(buffer.getLong(TAIL_COUNTER_INDEX)).thenReturn(tail); final UnsafeBuffer srcBuffer = new UnsafeBuffer(new byte[1024]); final int srcIndex = 0; broadcastTransmitter.transmit(MSG_TYPE_ID, srcBuffer, srcIndex, length); final InOrder inOrder = inOrder(buffer); inOrder.verify(buffer).getLong(TAIL_COUNTER_INDEX); inOrder.verify(buffer).putLongOrdered(TAIL_INTENT_COUNTER_OFFSET, tail + recordLengthAligned); inOrder.verify(buffer).putInt(lengthOffset(recordOffset), recordLength); inOrder.verify(buffer).putInt(typeOffset(recordOffset), MSG_TYPE_ID); inOrder.verify(buffer).putBytes(msgOffset(recordOffset), srcBuffer, srcIndex, length); inOrder.verify(buffer).putLong(LATEST_COUNTER_INDEX, tail); inOrder.verify(buffer).putLongOrdered(TAIL_COUNTER_INDEX, tail + recordLengthAligned); } @Test public void shouldTransmitIntoEndOfBuffer() { final int length = 8; final int recordLength = length + HEADER_LENGTH; final int recordLengthAligned = align(recordLength, RECORD_ALIGNMENT); final long tail = CAPACITY - recordLengthAligned; final int recordOffset = (int)tail; when(buffer.getLong(TAIL_COUNTER_INDEX)).thenReturn(tail); final UnsafeBuffer srcBuffer = new UnsafeBuffer(new byte[1024]); final int srcIndex = 0; broadcastTransmitter.transmit(MSG_TYPE_ID, srcBuffer, srcIndex, length); final InOrder inOrder = inOrder(buffer); inOrder.verify(buffer).getLong(TAIL_COUNTER_INDEX); inOrder.verify(buffer).putLongOrdered(TAIL_INTENT_COUNTER_OFFSET, tail + recordLengthAligned); inOrder.verify(buffer).putInt(lengthOffset(recordOffset), recordLength); inOrder.verify(buffer).putInt(typeOffset(recordOffset), MSG_TYPE_ID); inOrder.verify(buffer).putBytes(msgOffset(recordOffset), srcBuffer, srcIndex, length); inOrder.verify(buffer).putLong(LATEST_COUNTER_INDEX, tail); inOrder.verify(buffer).putLongOrdered(TAIL_COUNTER_INDEX, tail + recordLengthAligned); } @Test public void shouldApplyPaddingWhenInsufficientSpaceAtEndOfBuffer() { long tail = CAPACITY - RECORD_ALIGNMENT; int recordOffset = (int)tail; final int length = RECORD_ALIGNMENT + 8; final int recordLength = length + HEADER_LENGTH; final int recordLengthAligned = align(recordLength, RECORD_ALIGNMENT); final int toEndOfBuffer = CAPACITY - recordOffset; when(buffer.getLong(TAIL_COUNTER_INDEX)).thenReturn(tail); final UnsafeBuffer srcBuffer = new UnsafeBuffer(new byte[1024]); final int srcIndex = 0; broadcastTransmitter.transmit(MSG_TYPE_ID, srcBuffer, srcIndex, length); final InOrder inOrder = inOrder(buffer); inOrder.verify(buffer).getLong(TAIL_COUNTER_INDEX); inOrder.verify(buffer).putLongOrdered(TAIL_INTENT_COUNTER_OFFSET, tail + recordLengthAligned + toEndOfBuffer); inOrder.verify(buffer).putInt(lengthOffset(recordOffset), toEndOfBuffer); inOrder.verify(buffer).putInt(typeOffset(recordOffset), PADDING_MSG_TYPE_ID); tail += toEndOfBuffer; recordOffset = 0; inOrder.verify(buffer).putInt(lengthOffset(recordOffset), recordLength); inOrder.verify(buffer).putInt(typeOffset(recordOffset), MSG_TYPE_ID); inOrder.verify(buffer).putBytes(msgOffset(recordOffset), srcBuffer, srcIndex, length); inOrder.verify(buffer).putLong(LATEST_COUNTER_INDEX, tail); inOrder.verify(buffer).putLongOrdered(TAIL_COUNTER_INDEX, tail + recordLengthAligned); } }