private void writeAndCheckLog(Log log, long logCapacity, long segmentCapacity, long segmentsLeft, long writeSize,
      List<LogSegmentName> segmentNames, int activeSegmentIdx, Appender appender) throws StoreException {
    byte[] buf = TestUtils.getRandomBytes((int) writeSize);
    long expectedUsedCapacity = logCapacity - segmentCapacity * segmentsLeft;
    int nextSegmentIdx = activeSegmentIdx + 1;
    LogSegment expectedActiveSegment = log.getSegment(segmentNames.get(activeSegmentIdx));
    LogSegmentName activeSegName = expectedActiveSegment.getName();
    // add header space (if any) from the active segment.
    long currentSegmentWriteSize = expectedActiveSegment.getEndOffset();
    expectedUsedCapacity += currentSegmentWriteSize;
    while (expectedUsedCapacity + writeSize <= logCapacity) {
      Offset endOffsetOfLastMessage = new Offset(activeSegName, currentSegmentWriteSize);
      appender.append(log, ByteBuffer.wrap(buf));
      FileSpan fileSpanOfMessage = log.getFileSpanForMessage(endOffsetOfLastMessage, writeSize);
      FileSpan expectedFileSpanForMessage =
          new FileSpan(endOffsetOfLastMessage, new Offset(activeSegName, currentSegmentWriteSize + writeSize));
      currentSegmentWriteSize += writeSize;
      expectedUsedCapacity += writeSize;
      if (currentSegmentWriteSize > segmentCapacity) {
        // calculate the end offset to ensure no partial writes.
        currentSegmentWriteSize =
            LogSegment.HEADER_SIZE + currentSegmentWriteSize - expectedActiveSegment.getEndOffset();
        // add the "wasted" space to expectedUsedCapacity
        expectedUsedCapacity += LogSegment.HEADER_SIZE + segmentCapacity - expectedActiveSegment.getEndOffset();
        expectedActiveSegment = log.getSegment(segmentNames.get(nextSegmentIdx));
        assertNotNull("Next active segment is null", expectedActiveSegment);
        activeSegName = expectedActiveSegment.getName();
        // currentSegmentWriteSize must be equal to LogSegment.HEADER_SIZE + writeSize
        assertEquals("Unexpected size of new active segment", writeSize + LogSegment.HEADER_SIZE,
            currentSegmentWriteSize);
        expectedFileSpanForMessage = new FileSpan(new Offset(activeSegName, expectedActiveSegment.getStartOffset()),
            new Offset(activeSegName, currentSegmentWriteSize));
        nextSegmentIdx++;
      }
      assertEquals("StartOffset of message  not as expected", expectedFileSpanForMessage.getStartOffset(),
          fileSpanOfMessage.getStartOffset());
      assertEquals("EndOffset of message  not as expected", expectedFileSpanForMessage.getEndOffset(),
          fileSpanOfMessage.getEndOffset());
      assertEquals("Active segment end offset not as expected", currentSegmentWriteSize,
          expectedActiveSegment.getEndOffset());
      assertEquals("End offset not as expected", new Offset(expectedActiveSegment.getName(), currentSegmentWriteSize),
          log.getEndOffset());
    }
    // try one more write that should fail
    try {
      appender.append(log, ByteBuffer.wrap(buf));
      fail("Should have failed because max capacity has been reached");
    } catch (IllegalArgumentException | IllegalStateException e) {
      // expected. Nothing to do.
    }
  }
Writes data to the log and checks for end offset (segment and log), roll over and used capacity.
@param log the {@link Log} instance to use.
@param logCapacity the total capacity of the {@code log}.
@param segmentCapacity the capacity of each segment in the {@code log}.
@param segmentsLeft the number of segments in the log that still have capacity remaining.
@param writeSize the size of each write to the log.
@param segmentNames the names of *all* the segments in the log including the ones that may not yet have been
                    created.
@param activeSegmentIdx the index of the name of the active segment in {@code segmentNames}. Also its absolute
                        index in the {@link Log}.
@param appender the {@link Appender} to use.
@throws StoreException
ambry-store/src/test/java/com/github/ambry/store/LogTest.java