Back to Repositories

Testing RingBuffer Sequence Operations in LMAX-Exchange/disruptor

This test suite validates the RingBuffer implementation with an asserting stub sequencer in the LMAX Disruptor library. It focuses on testing delegation of sequence generation and publishing operations through a custom AssertingSequencer implementation.

Test Coverage Overview

The test suite provides comprehensive coverage of RingBuffer’s sequence generation and publishing operations.

  • Tests single and batch sequence generation
  • Validates both regular and try-based operations
  • Covers sequence publishing with range validation
  • Verifies batch size assertions and sequence tracking

Implementation Analysis

The testing approach uses a custom AssertingSequencer that implements the Sequencer interface to validate RingBuffer operations. It employs JUnit Jupiter annotations and assertions to verify sequence generation and publishing behavior.

  • Uses ThreadLocalRandom for sequence generation
  • Implements comprehensive interface contract verification
  • Validates sequence ranges and batch sizes

Technical Details

  • JUnit Jupiter test framework
  • Hamcrest matchers for assertions
  • Custom stub implementation of Sequencer interface
  • ThreadLocalRandom for sequence generation
  • BeforeEach setup with 16-element buffer size

Best Practices Demonstrated

The test suite exemplifies several testing best practices in Java unit testing.

  • Clear test method naming conventions
  • Proper test setup isolation
  • Custom stub implementation for controlled testing
  • Comprehensive interface contract validation
  • Effective use of assertion frameworks

lmax-exchange/disruptor

src/test/java/com/lmax/disruptor/RingBufferWithAssertingStubTest.java

            
package com.lmax.disruptor;

import com.lmax.disruptor.support.StubEvent;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.util.concurrent.ThreadLocalRandom;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;

public class RingBufferWithAssertingStubTest
{
    private RingBuffer<StubEvent> ringBuffer;
    private Sequencer sequencer;

    @BeforeEach
    public void setUp()
    {
        sequencer = new AssertingSequencer(16);

        ringBuffer = new RingBuffer<>(StubEvent.EVENT_FACTORY, sequencer);
    }

    @Test
    public void shouldDelegateNextAndPublish()
    {
        ringBuffer.publish(ringBuffer.next());
    }

    @Test
    public void shouldDelegateTryNextAndPublish() throws Exception
    {
        ringBuffer.publish(ringBuffer.tryNext());
    }

    @Test
    public void shouldDelegateNextNAndPublish() throws Exception
    {
        long hi = ringBuffer.next(10);
        ringBuffer.publish(hi - 9, hi);
    }

    @Test
    public void shouldDelegateTryNextNAndPublish() throws Exception
    {
        long hi = ringBuffer.tryNext(10);
        ringBuffer.publish(hi - 9, hi);
    }

    private static final class AssertingSequencer implements Sequencer
    {
        private final int size;
        private long lastBatchSize = -1;
        private long lastValue = -1;

        private AssertingSequencer(final int size)
        {
            this.size = size;
        }

        @Override
        public int getBufferSize()
        {
            return size;
        }

        @Override
        public boolean hasAvailableCapacity(final int requiredCapacity)
        {
            return requiredCapacity <= size;
        }

        @Override
        public long remainingCapacity()
        {
            return size;
        }

        @Override
        public long next()
        {
            lastValue = ThreadLocalRandom.current().nextLong(0, 1000000);
            lastBatchSize = 1;
            return lastValue;
        }

        @Override
        public long next(final int n)
        {
            lastValue = ThreadLocalRandom.current().nextLong(n, 1000000);
            lastBatchSize = n;
            return lastValue;
        }

        @Override
        public long tryNext() throws InsufficientCapacityException
        {
            return next();
        }

        @Override
        public long tryNext(final int n) throws InsufficientCapacityException
        {
            return next(n);
        }

        @Override
        public void publish(final long sequence)
        {
            assertThat(sequence, is(lastValue));
            assertThat(lastBatchSize, is(1L));
        }

        @Override
        public void publish(final long lo, final long hi)
        {
            assertThat(hi, is(lastValue));
            assertThat((hi - lo) + 1, is(lastBatchSize));
        }

        @Override
        public long getCursor()
        {
            return lastValue;
        }

        @Override
        public void claim(final long sequence)
        {

        }

        @Override
        public boolean isAvailable(final long sequence)
        {
            return false;
        }

        @Override
        public void addGatingSequences(final Sequence... gatingSequences)
        {

        }

        @Override
        public boolean removeGatingSequence(final Sequence sequence)
        {
            return false;
        }

        @Override
        public SequenceBarrier newBarrier(final Sequence... sequencesToTrack)
        {
            return null;
        }

        @Override
        public long getMinimumSequence()
        {
            return 0;
        }

        @Override
        public long getHighestPublishedSequence(final long nextSequence, final long availableSequence)
        {
            return 0;
        }

        @Override
        public <T> EventPoller<T> newPoller(final DataProvider<T> provider, final Sequence... gatingSequences)
        {
            return null;
        }
    }
}