/* * 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; import org.junit.Before; import org.junit.Test; import io.aeron.logbuffer.BufferClaim; import io.aeron.logbuffer.FrameDescriptor; import org.agrona.concurrent.UnsafeBuffer; import org.agrona.concurrent.status.ReadablePosition; import org.mockito.InOrder; import org.mockito.Mockito; import java.nio.ByteBuffer; import java.util.concurrent.locks.Lock; import static java.nio.ByteBuffer.allocateDirect; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.*; import static io.aeron.logbuffer.LogBufferDescriptor.*; public class PublicationTest { private static final String CHANNEL = "aeron:udp?endpoint=localhost:40124"; private static final int STREAM_ID_1 = 2; private static final int SESSION_ID_1 = 13; private static final int TERM_ID_1 = 1; private static final int CORRELATION_ID = 2000; private static final int SEND_BUFFER_CAPACITY = 1024; private static final int PARTITION_INDEX = 0; private static final int MTU_LENGTH = 4096; private final ByteBuffer sendBuffer = allocateDirect(SEND_BUFFER_CAPACITY); private final UnsafeBuffer atomicSendBuffer = new UnsafeBuffer(sendBuffer); private final UnsafeBuffer logMetaDataBuffer = spy(new UnsafeBuffer(allocateDirect(LOG_META_DATA_LENGTH))); private final UnsafeBuffer[] termBuffers = new UnsafeBuffer[PARTITION_COUNT]; private final Lock conductorLock = mock(Lock.class); private final ClientConductor conductor = mock(ClientConductor.class); private final LogBuffers logBuffers = mock(LogBuffers.class); private final ReadablePosition publicationLimit = mock(ReadablePosition.class); private Publication publication; @Before public void setUp() { when(publicationLimit.getVolatile()).thenReturn(2L * SEND_BUFFER_CAPACITY); when(logBuffers.termBuffers()).thenReturn(termBuffers); when(logBuffers.termLength()).thenReturn(TERM_MIN_LENGTH); when(logBuffers.metaDataBuffer()).thenReturn(logMetaDataBuffer); when(conductor.clientLock()).thenReturn(conductorLock); initialTermId(logMetaDataBuffer, TERM_ID_1); mtuLength(logMetaDataBuffer, MTU_LENGTH); timeOfLastStatusMessage(logMetaDataBuffer, 0); for (int i = 0; i < PARTITION_COUNT; i++) { termBuffers[i] = new UnsafeBuffer(allocateDirect(TERM_MIN_LENGTH)); } publication = new Publication( conductor, CHANNEL, STREAM_ID_1, SESSION_ID_1, publicationLimit, logBuffers, CORRELATION_ID); publication.incRef(); initialiseTailWithTermId(logMetaDataBuffer, PARTITION_INDEX, TERM_ID_1); } @Test public void shouldEnsureThePublicationIsOpenBeforeReadingPosition() { publication.close(); assertThat(publication.position(), is(Publication.CLOSED)); final InOrder inOrder = Mockito.inOrder(conductorLock, conductor); inOrder.verify(conductorLock).lock(); inOrder.verify(conductor).releasePublication(publication); inOrder.verify(conductorLock).unlock(); } @Test public void shouldEnsureThePublicationIsOpenBeforeOffer() { publication.close(); assertTrue(publication.isClosed()); assertThat(publication.offer(atomicSendBuffer), is(Publication.CLOSED)); } @Test public void shouldEnsureThePublicationIsOpenBeforeClaim() { publication.close(); final BufferClaim bufferClaim = new BufferClaim(); assertThat(publication.tryClaim(SEND_BUFFER_CAPACITY, bufferClaim), is(Publication.CLOSED)); } @Test public void shouldReportThatPublicationHasNotBeenConnectedYet() { when(publicationLimit.getVolatile()).thenReturn(0L); when(conductor.isPublicationConnected(anyLong())).thenReturn(false); assertFalse(publication.isConnected()); } @Test public void shouldReportThatPublicationHasBeenConnectedYet() { when(conductor.isPublicationConnected(anyLong())).thenReturn(true); assertTrue(publication.isConnected()); } @Test public void shouldReportInitialPosition() { assertThat(publication.position(), is(0L)); } @Test public void shouldReportMaxMessageLength() { assertThat(publication.maxMessageLength(), is(FrameDescriptor.computeMaxMessageLength(TERM_MIN_LENGTH))); } @Test public void shouldNotUnmapBuffersBeforeLastRelease() throws Exception { publication.incRef(); publication.close(); verify(logBuffers, never()).close(); final InOrder inOrder = Mockito.inOrder(conductorLock, conductor); inOrder.verify(conductorLock).lock(); inOrder.verify(conductorLock).unlock(); inOrder.verifyNoMoreInteractions(); } @Test public void shouldUnmapBuffersWithMultipleReferences() throws Exception { publication.incRef(); publication.close(); publication.close(); final InOrder inOrder = Mockito.inOrder(conductorLock, conductor); inOrder.verify(conductorLock).lock(); inOrder.verify(conductor).releasePublication(publication); inOrder.verify(conductorLock).unlock(); inOrder.verifyNoMoreInteractions(); } @Test public void shouldReleaseResourcesIdempotently() throws Exception { publication.close(); publication.close(); final InOrder inOrder = Mockito.inOrder(conductorLock, conductor); inOrder.verify(conductorLock).lock(); inOrder.verify(conductor).releasePublication(publication); inOrder.verify(conductorLock).unlock(); inOrder.verify(conductorLock).lock(); inOrder.verify(conductorLock).unlock(); inOrder.verifyNoMoreInteractions(); } }