/*
* 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.logbuffer;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InOrder;
import org.agrona.concurrent.UnsafeBuffer;
import static java.nio.ByteOrder.LITTLE_ENDIAN;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.*;
import static io.aeron.logbuffer.FrameDescriptor.termOffsetOffset;
import static io.aeron.logbuffer.FrameDescriptor.typeOffset;
import static io.aeron.logbuffer.TermUnblocker.Status.NO_ACTION;
import static io.aeron.logbuffer.TermUnblocker.Status.UNBLOCKED;
import static io.aeron.logbuffer.TermUnblocker.Status.UNBLOCKED_TO_END;
import static io.aeron.protocol.HeaderFlyweight.HDR_TYPE_PAD;
import static io.aeron.protocol.HeaderFlyweight.HEADER_LENGTH;
public class TermUnblockerTest
{
private static final int TERM_BUFFER_CAPACITY = 64 * 1014;
private static final int TERM_ID = 7;
private final UnsafeBuffer mockTermBuffer = mock(UnsafeBuffer.class);
private final UnsafeBuffer mockLogMetaDataBuffer = mock(UnsafeBuffer.class);
@Before
public void setUp()
{
when(mockTermBuffer.capacity()).thenReturn(TERM_BUFFER_CAPACITY);
}
@Test
public void shouldTakeNoActionWhenMessageIsComplete()
{
final int termOffset = 0;
final int tailOffset = TERM_BUFFER_CAPACITY;
when(mockTermBuffer.getIntVolatile(termOffset)).thenReturn(HEADER_LENGTH);
assertThat(TermUnblocker.unblock(
mockLogMetaDataBuffer, mockTermBuffer, termOffset, tailOffset, TERM_ID), is(NO_ACTION));
}
@Test
public void shouldTakeNoActionWhenNoUnblockedMessage()
{
final int termOffset = 0;
final int tailOffset = TERM_BUFFER_CAPACITY / 2;
assertThat(TermUnblocker.unblock(
mockLogMetaDataBuffer, mockTermBuffer, termOffset, tailOffset, TERM_ID), is(NO_ACTION));
}
@Test
public void shouldPatchNonCommittedMessage()
{
final int termOffset = 0;
final int messageLength = HEADER_LENGTH * 4;
final int tailOffset = messageLength;
when(mockTermBuffer.getIntVolatile(termOffset)).thenReturn(-messageLength);
assertThat(TermUnblocker.unblock(
mockLogMetaDataBuffer, mockTermBuffer, termOffset, tailOffset, TERM_ID), is(UNBLOCKED));
final InOrder inOrder = inOrder(mockTermBuffer);
inOrder.verify(mockTermBuffer).putShort(typeOffset(termOffset), (short)HDR_TYPE_PAD, LITTLE_ENDIAN);
inOrder.verify(mockTermBuffer).putInt(termOffsetOffset(termOffset), termOffset, LITTLE_ENDIAN);
inOrder.verify(mockTermBuffer).putIntOrdered(termOffset, messageLength);
}
@Test
public void shouldPatchToEndOfPartition()
{
final int messageLength = HEADER_LENGTH * 4;
final int termOffset = TERM_BUFFER_CAPACITY - messageLength;
final int tailOffset = TERM_BUFFER_CAPACITY;
when(mockTermBuffer.getIntVolatile(termOffset)).thenReturn(0);
assertThat(TermUnblocker.unblock(
mockLogMetaDataBuffer, mockTermBuffer, termOffset, tailOffset, TERM_ID), is(UNBLOCKED_TO_END));
final InOrder inOrder = inOrder(mockTermBuffer);
inOrder.verify(mockTermBuffer).putShort(typeOffset(termOffset), (short)HDR_TYPE_PAD, LITTLE_ENDIAN);
inOrder.verify(mockTermBuffer).putInt(termOffsetOffset(termOffset), termOffset, LITTLE_ENDIAN);
inOrder.verify(mockTermBuffer).putIntOrdered(termOffset, messageLength);
}
@Test
public void shouldScanForwardForNextCompleteMessage()
{
final int messageLength = HEADER_LENGTH * 4;
final int termOffset = 0;
final int tailOffset = messageLength * 2;
when(mockTermBuffer.getIntVolatile(messageLength)).thenReturn(messageLength);
assertThat(TermUnblocker.unblock(
mockLogMetaDataBuffer, mockTermBuffer, termOffset, tailOffset, TERM_ID), is(UNBLOCKED));
final InOrder inOrder = inOrder(mockTermBuffer);
inOrder.verify(mockTermBuffer).putShort(typeOffset(termOffset), (short)HDR_TYPE_PAD, LITTLE_ENDIAN);
inOrder.verify(mockTermBuffer).putInt(termOffsetOffset(termOffset), termOffset, LITTLE_ENDIAN);
inOrder.verify(mockTermBuffer).putIntOrdered(termOffset, messageLength);
}
@Test
public void shouldScanForwardForNextNonCommittedMessage()
{
final int messageLength = HEADER_LENGTH * 4;
final int termOffset = 0;
final int tailOffset = messageLength * 2;
when(mockTermBuffer.getIntVolatile(messageLength)).thenReturn(-messageLength);
assertThat(TermUnblocker.unblock(
mockLogMetaDataBuffer, mockTermBuffer, termOffset, tailOffset, TERM_ID), is(UNBLOCKED));
final InOrder inOrder = inOrder(mockTermBuffer);
inOrder.verify(mockTermBuffer).putShort(typeOffset(termOffset), (short)HDR_TYPE_PAD, LITTLE_ENDIAN);
inOrder.verify(mockTermBuffer).putInt(termOffsetOffset(termOffset), termOffset, LITTLE_ENDIAN);
inOrder.verify(mockTermBuffer).putIntOrdered(termOffset, messageLength);
}
@Test
public void shouldTakeNoActionIfMessageCompleteAfterScan()
{
final int messageLength = HEADER_LENGTH * 4;
final int termOffset = 0;
final int tailOffset = messageLength * 2;
when(mockTermBuffer.getIntVolatile(termOffset))
.thenReturn(0)
.thenReturn(messageLength);
when(mockTermBuffer.getIntVolatile(messageLength))
.thenReturn(messageLength);
assertThat(TermUnblocker.unblock(
mockLogMetaDataBuffer, mockTermBuffer, termOffset, tailOffset, TERM_ID), is(NO_ACTION));
}
@Test
public void shouldTakeNoActionIfMessageNonCommittedAfterScan()
{
final int messageLength = HEADER_LENGTH * 4;
final int termOffset = 0;
final int tailOffset = messageLength * 2;
when(mockTermBuffer.getIntVolatile(termOffset))
.thenReturn(0)
.thenReturn(-messageLength);
when(mockTermBuffer.getIntVolatile(messageLength))
.thenReturn(messageLength);
assertThat(TermUnblocker.unblock(
mockLogMetaDataBuffer, mockTermBuffer, termOffset, tailOffset, TERM_ID), is(NO_ACTION));
}
@Test
public void shouldTakeNoActionToEndOfPartitionIfMessageCompleteAfterScan()
{
final int messageLength = HEADER_LENGTH * 4;
final int termOffset = TERM_BUFFER_CAPACITY - messageLength;
final int tailOffset = TERM_BUFFER_CAPACITY;
when(mockTermBuffer.getIntVolatile(termOffset))
.thenReturn(0)
.thenReturn(messageLength);
assertThat(TermUnblocker.unblock(
mockLogMetaDataBuffer, mockTermBuffer, termOffset, tailOffset, TERM_ID), is(NO_ACTION));
}
@Test
public void shouldTakeNoActionToEndOfPartitionIfMessageNonCommittedAfterScan()
{
final int messageLength = HEADER_LENGTH * 4;
final int termOffset = TERM_BUFFER_CAPACITY - messageLength;
final int tailOffset = TERM_BUFFER_CAPACITY;
when(mockTermBuffer.getIntVolatile(termOffset))
.thenReturn(0)
.thenReturn(-messageLength);
assertThat(TermUnblocker.unblock(
mockLogMetaDataBuffer, mockTermBuffer, termOffset, tailOffset, TERM_ID), is(NO_ACTION));
}
@Test
public void shouldNotUnblockGapWithMessageRaceOnSecondMessageIncreasingTailThenInterrupting()
{
final int messageLength = HEADER_LENGTH * 4;
final int termOffset = 0;
final int tailOffset = messageLength * 3;
when(mockTermBuffer.getIntVolatile(messageLength))
.thenReturn(0)
.thenReturn(messageLength);
when(mockTermBuffer.getIntVolatile(messageLength * 2))
.thenReturn(messageLength);
assertThat(TermUnblocker.unblock(
mockLogMetaDataBuffer, mockTermBuffer, termOffset, tailOffset, TERM_ID), is(NO_ACTION));
}
@Test
public void shouldNotUnblockGapWithMessageRaceWhenScanForwardTakesAnInterrupt()
{
final int messageLength = HEADER_LENGTH * 4;
final int termOffset = 0;
final int tailOffset = messageLength * 3;
when(mockTermBuffer.getIntVolatile(messageLength))
.thenReturn(0)
.thenReturn(messageLength);
when(mockTermBuffer.getIntVolatile(messageLength + HEADER_LENGTH))
.thenReturn(7);
assertThat(TermUnblocker.unblock(
mockLogMetaDataBuffer, mockTermBuffer, termOffset, tailOffset, TERM_ID), is(NO_ACTION));
}
}