@Test
  public void shouldPadLogAndTripWhenAppendingWithInsufficientRemainingCapacityIncludingHeader() {
    final int headerLength = DEFAULT_HEADER.capacity();
    final int msgLength = 120;
    final int requiredFrameSize = align(headerLength + msgLength, FRAME_ALIGNMENT);
    final int tailValue =
        termAppender.termBuffer().capacity()
            - (requiredFrameSize + (headerLength - FRAME_ALIGNMENT));
    final UnsafeBuffer buffer = new UnsafeBuffer(new byte[128]);
    final int frameLength = TERM_BUFFER_LENGTH - tailValue;

    when(metaDataBuffer.getAndAddInt(TERM_TAIL_COUNTER_OFFSET, requiredFrameSize))
        .thenReturn(tailValue);

    assertThat(termAppender.append(buffer, 0, msgLength), is(TermAppender.TRIPPED));

    final InOrder inOrder = inOrder(termBuffer, metaDataBuffer);
    inOrder
        .verify(metaDataBuffer, times(1))
        .getAndAddInt(TERM_TAIL_COUNTER_OFFSET, requiredFrameSize);
    verifyDefaultHeader(inOrder, termBuffer, tailValue, frameLength);
    inOrder
        .verify(termBuffer, times(1))
        .putShort(typeOffset(tailValue), (short) PADDING_FRAME_TYPE, LITTLE_ENDIAN);
    inOrder
        .verify(termBuffer, times(1))
        .putInt(termOffsetOffset(tailValue), tailValue, LITTLE_ENDIAN);
    inOrder.verify(termBuffer, times(1)).putIntOrdered(tailValue, frameLength);
  }
  @Test(expected = IllegalArgumentException.class)
  public void shouldThrowExceptionWhenMaxMessageLengthExceeded() {
    final int maxMessageLength = termAppender.maxMessageLength();
    final UnsafeBuffer srcBuffer = new UnsafeBuffer(new byte[1024]);

    termAppender.append(srcBuffer, 0, maxMessageLength + 1);
  }
  @Test
  public void shouldReportCurrentTailAtCapacity() {
    final int tailValue = TERM_BUFFER_LENGTH + 64;

    when(metaDataBuffer.getIntVolatile(TERM_TAIL_COUNTER_OFFSET)).thenReturn(tailValue);
    when(metaDataBuffer.getInt(TERM_TAIL_COUNTER_OFFSET)).thenReturn(tailValue);

    assertThat(termAppender.tailVolatile(), is(TERM_BUFFER_LENGTH));
    assertThat(termAppender.tail(), is(TERM_BUFFER_LENGTH));
  }
  @Test
  public void shouldClaimRegionForZeroCopyEncoding() {
    final int headerLength = DEFAULT_HEADER.capacity();
    final int msgLength = 20;
    final int frameLength = msgLength + headerLength;
    final int alignedFrameLength = align(frameLength, FRAME_ALIGNMENT);
    final int tail = 0;
    final BufferClaim bufferClaim = new BufferClaim();

    when(metaDataBuffer.getAndAddInt(TERM_TAIL_COUNTER_OFFSET, alignedFrameLength)).thenReturn(0);

    assertThat(termAppender.claim(msgLength, bufferClaim), is(alignedFrameLength));

    assertThat(bufferClaim.offset(), is(tail + headerLength));
    assertThat(bufferClaim.length(), is(msgLength));

    // Map flyweight or encode to buffer directly then call commit() when done
    bufferClaim.commit();

    final InOrder inOrder = inOrder(termBuffer, metaDataBuffer);
    inOrder
        .verify(metaDataBuffer, times(1))
        .getAndAddInt(TERM_TAIL_COUNTER_OFFSET, alignedFrameLength);
    verifyDefaultHeader(inOrder, termBuffer, tail, frameLength);
    inOrder.verify(termBuffer, times(1)).putInt(termOffsetOffset(tail), tail, LITTLE_ENDIAN);
  }
  @Test
  public void shouldAppendFrameTwiceToLog() {
    final int headerLength = DEFAULT_HEADER.capacity();
    final UnsafeBuffer buffer = new UnsafeBuffer(new byte[128]);
    final int msgLength = 20;
    final int frameLength = msgLength + headerLength;
    final int alignedFrameLength = align(frameLength, FRAME_ALIGNMENT);
    int tail = 0;

    when(metaDataBuffer.getAndAddInt(TERM_TAIL_COUNTER_OFFSET, alignedFrameLength))
        .thenReturn(0)
        .thenReturn(alignedFrameLength);

    assertThat(termAppender.append(buffer, 0, msgLength), is(alignedFrameLength));
    assertThat(termAppender.append(buffer, 0, msgLength), is(alignedFrameLength * 2));

    final InOrder inOrder = inOrder(termBuffer, metaDataBuffer);
    inOrder
        .verify(metaDataBuffer, times(1))
        .getAndAddInt(TERM_TAIL_COUNTER_OFFSET, alignedFrameLength);
    verifyDefaultHeader(inOrder, termBuffer, tail, frameLength);
    inOrder.verify(termBuffer, times(1)).putBytes(headerLength, buffer, 0, msgLength);
    inOrder.verify(termBuffer, times(1)).putInt(termOffsetOffset(tail), tail, LITTLE_ENDIAN);
    inOrder.verify(termBuffer, times(1)).putIntOrdered(tail, frameLength);

    tail = alignedFrameLength;
    inOrder
        .verify(metaDataBuffer, times(1))
        .getAndAddInt(TERM_TAIL_COUNTER_OFFSET, alignedFrameLength);
    verifyDefaultHeader(inOrder, termBuffer, tail, frameLength);
    inOrder.verify(termBuffer, times(1)).putBytes(tail + headerLength, buffer, 0, msgLength);
    inOrder.verify(termBuffer, times(1)).putInt(termOffsetOffset(tail), tail, LITTLE_ENDIAN);
    inOrder.verify(termBuffer, times(1)).putIntOrdered(tail, frameLength);
  }
  @Test
  public void shouldFragmentMessageOverTwoFrames() {
    final int msgLength = termAppender.maxPayloadLength() + 1;
    final int headerLength = DEFAULT_HEADER.capacity();
    final int frameLength = headerLength + 1;
    final int requiredCapacity =
        align(headerLength + 1, FRAME_ALIGNMENT) + termAppender.maxFrameLength();
    final UnsafeBuffer buffer = new UnsafeBuffer(new byte[msgLength]);

    when(metaDataBuffer.getAndAddInt(TERM_TAIL_COUNTER_OFFSET, requiredCapacity)).thenReturn(0);

    assertThat(termAppender.append(buffer, 0, msgLength), is(requiredCapacity));

    int tail = 0;
    final InOrder inOrder = inOrder(termBuffer, metaDataBuffer);
    inOrder
        .verify(metaDataBuffer, times(1))
        .getAndAddInt(TERM_TAIL_COUNTER_OFFSET, requiredCapacity);

    verifyDefaultHeader(inOrder, termBuffer, tail, termAppender.maxFrameLength());
    inOrder
        .verify(termBuffer, times(1))
        .putBytes(tail + headerLength, buffer, 0, termAppender.maxPayloadLength());
    inOrder.verify(termBuffer, times(1)).putByte(flagsOffset(tail), BEGIN_FRAG);
    inOrder.verify(termBuffer, times(1)).putInt(termOffsetOffset(tail), tail, LITTLE_ENDIAN);
    inOrder.verify(termBuffer, times(1)).putIntOrdered(tail, termAppender.maxFrameLength());

    tail = termAppender.maxFrameLength();
    verifyDefaultHeader(inOrder, termBuffer, tail, frameLength);
    inOrder
        .verify(termBuffer, times(1))
        .putBytes(tail + headerLength, buffer, termAppender.maxPayloadLength(), 1);
    inOrder.verify(termBuffer, times(1)).putByte(flagsOffset(tail), END_FRAG);
    inOrder.verify(termBuffer, times(1)).putInt(termOffsetOffset(tail), tail, LITTLE_ENDIAN);
    inOrder.verify(termBuffer, times(1)).putIntOrdered(tail, frameLength);
  }
 @Test
 public void shouldReportMaxFrameLength() {
   assertThat(termAppender.maxFrameLength(), is(MAX_FRAME_LENGTH));
 }
 @Test
 public void shouldReportCapacity() {
   assertThat(termAppender.termBuffer().capacity(), is(TERM_BUFFER_LENGTH));
 }