org.dcache.xrootd.stream.ChunkedFileChannelReadvResponseTest.java Source code

Java tutorial

Introduction

Here is the source code for org.dcache.xrootd.stream.ChunkedFileChannelReadvResponseTest.java

Source

/**
 * Copyright (C) 2011-2018 dCache.org <support@dcache.org>
 *
 * This file is part of xrootd4j.
 *
 * xrootd4j is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * xrootd4j is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with xrootd4j.  If not, see http://www.gnu.org/licenses/.
 */
package org.dcache.xrootd.stream;

import com.google.common.collect.Lists;
import io.netty.buffer.UnpooledByteBufAllocator;
import org.junit.Before;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Arrays;
import java.util.List;

import org.dcache.xrootd.protocol.messages.GenericReadRequestMessage;
import org.dcache.xrootd.protocol.messages.ReadVRequest;
import org.dcache.xrootd.protocol.messages.ReadVResponse;

import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.*;

public class ChunkedFileChannelReadvResponseTest {
    private static final int SOME_ID = 1234;
    private static final int SOME_FH = 1;
    private static final int HEADER = 16;

    private List<FileChannel> _channels;
    private GenericReadRequestMessage.EmbeddedReadRequest[] _requests;
    private ReadVRequest _request;

    @Before
    public void setUp() {
        _channels = Lists.newArrayList();
        _requests = new GenericReadRequestMessage.EmbeddedReadRequest[0];
        _request = mock(ReadVRequest.class);
        when(_request.getStreamId()).thenReturn(SOME_ID);
        when(_request.getReadRequestList()).thenReturn(_requests);
    }

    @Test
    public void shouldReturnSingleResponseIfAllowedByMaxFrameSize() throws Exception {
        givenFileDescriptor().withFileHandle(SOME_FH).withSize(10000);
        givenReadRequest().forFileHandle(SOME_FH).atOffset(100).forLength(200);
        givenReadRequest().forFileHandle(SOME_FH).atOffset(300).forLength(100);

        AbstractChunkedReadvResponse response = aResponseWithMaxFrameSizeOf(1024);
        ReadVResponse response1 = response.nextChunk(UnpooledByteBufAllocator.DEFAULT);
        ReadVResponse response2 = response.nextChunk(UnpooledByteBufAllocator.DEFAULT);

        assertThat(response1.getSegmentLengths(), hasItems(200, 100));
        assertThat(response2, is(nullValue()));
    }

    @Test(expected = IllegalStateException.class)
    public void shouldFailReadsBiggerThanMaxFrameSize() throws Exception {
        givenFileDescriptor().withFileHandle(SOME_FH).withSize(10000);
        givenReadRequest().forFileHandle(SOME_FH).atOffset(100).forLength(2000);

        AbstractChunkedReadvResponse response = aResponseWithMaxFrameSizeOf(1024);
        response.nextChunk(UnpooledByteBufAllocator.DEFAULT);
    }

    @Test
    public void shouldRespectMaxFrameSize() throws Exception {
        givenFileDescriptor().withFileHandle(SOME_FH).withSize(10000);
        givenReadRequest().forFileHandle(SOME_FH).atOffset(100).forLength(100);
        givenReadRequest().forFileHandle(SOME_FH).atOffset(300).forLength(1000);

        AbstractChunkedReadvResponse response = aResponseWithMaxFrameSizeOf(1024);
        ReadVResponse response1 = response.nextChunk(UnpooledByteBufAllocator.DEFAULT);
        ReadVResponse response2 = response.nextChunk(UnpooledByteBufAllocator.DEFAULT);
        ReadVResponse response3 = response.nextChunk(UnpooledByteBufAllocator.DEFAULT);

        assertThat(response1.getSegmentLengths(), hasItems(100, 100));
        assertThat(response2.getSegmentLengths(), hasItems(1000));
        assertThat(response3, is(nullValue()));
    }

    @Test
    public void shouldRespectEndOfFile() throws Exception {
        givenFileDescriptor().withFileHandle(SOME_FH).withSize(10000);
        givenReadRequest().forFileHandle(SOME_FH).atOffset(9700).forLength(500);

        AbstractChunkedReadvResponse response = aResponseWithMaxFrameSizeOf(1024);
        ReadVResponse response1 = response.nextChunk(UnpooledByteBufAllocator.DEFAULT);
        ReadVResponse response2 = response.nextChunk(UnpooledByteBufAllocator.DEFAULT);

        assertThat(response1.getSegmentLengths(), hasItems(300));
        assertThat(response2, is(nullValue()));
    }

    @Test
    public void shouldUsePositionIndependentRead() throws Exception {
        givenFileDescriptor().withFileHandle(SOME_FH).withSize(10000);
        givenReadRequest().forFileHandle(SOME_FH).atOffset(100).forLength(100);
        givenReadRequest().forFileHandle(SOME_FH).atOffset(200).forLength(100);
        givenReadRequest().forFileHandle(SOME_FH).atOffset(400).forLength(1000);
        givenReadRequest().forFileHandle(SOME_FH).atOffset(9700).forLength(1000);

        AbstractChunkedReadvResponse response = aResponseWithMaxFrameSizeOf(1024);
        ReadVResponse response1 = response.nextChunk(UnpooledByteBufAllocator.DEFAULT);
        ReadVResponse response2 = response.nextChunk(UnpooledByteBufAllocator.DEFAULT);
        ReadVResponse response3 = response.nextChunk(UnpooledByteBufAllocator.DEFAULT);

        verify(channel(SOME_FH)).read(any(ByteBuffer.class), eq(100L));
        verify(channel(SOME_FH)).read(any(ByteBuffer.class), eq(200L));
        verify(channel(SOME_FH)).read(any(ByteBuffer.class), eq(400L));
        verify(channel(SOME_FH)).read(any(ByteBuffer.class), eq(9700L));
    }

    @Test
    public void shouldPackTruncatedReadsInSingleFrameIfPossible() throws Exception {
        givenFileDescriptor().withFileHandle(SOME_FH).withSize(400);
        givenReadRequest().forFileHandle(SOME_FH).atOffset(100).forLength(200);
        givenReadRequest().forFileHandle(SOME_FH).atOffset(300).forLength(1000);

        AbstractChunkedReadvResponse response = aResponseWithMaxFrameSizeOf(1024);

        ReadVResponse response1 = response.nextChunk(UnpooledByteBufAllocator.DEFAULT);
        ReadVResponse response2 = response.nextChunk(UnpooledByteBufAllocator.DEFAULT);

        assertThat(response1.getSegmentLengths(), hasItems(200, 100));
        assertThat(response2, is(nullValue()));
    }

    @Test(expected = IllegalStateException.class)
    public void shouldNotOverflowWithLargeRequests() throws Exception {
        givenFileDescriptor().withFileHandle(SOME_FH).withSize(Integer.MAX_VALUE);
        givenReadRequest().forFileHandle(SOME_FH).atOffset(0).forLength(Integer.MAX_VALUE);

        AbstractChunkedReadvResponse response = aResponseWithMaxFrameSizeOf(1024);

        ReadVResponse response1 = response.nextChunk(UnpooledByteBufAllocator.DEFAULT);
    }

    private FileDescriptorMaker givenFileDescriptor() {
        return new FileDescriptorMaker();
    }

    private ReadRequestMaker givenReadRequest() {
        int idx = _requests.length;
        _requests = Arrays.copyOf(_requests, _requests.length + 1);
        _requests[idx] = mock(GenericReadRequestMessage.EmbeddedReadRequest.class);
        _request = mock(ReadVRequest.class);
        when(_request.getStreamId()).thenReturn(SOME_ID);
        when(_request.getReadRequestList()).thenReturn(_requests);
        return new ReadRequestMaker(_requests[idx]);
    }

    private FileChannel channel(int fd) {
        return _channels.get(fd);
    }

    private AbstractChunkedReadvResponse aResponseWithMaxFrameSizeOf(int maxFrameSize) {
        return new ChunkedFileChannelReadvResponse(_request, maxFrameSize, _channels);
    }

    /** A builder of FileDescriptor with a fluent interface. */
    private class FileDescriptorMaker {
        private final FileChannel channel = mock(FileChannel.class);

        public FileDescriptorMaker() {
        }

        public FileDescriptorMaker withFileHandle(int fh) {
            while (fh >= _channels.size()) {
                _channels.add(null);
            }
            _channels.set(fh, channel);
            return this;
        }

        public FileDescriptorMaker withSize(final long length) throws IOException {
            when(channel.size()).thenReturn(length);
            when(channel.read(any(ByteBuffer.class), anyInt())).thenAnswer(new Answer() {
                @Override
                public Object answer(InvocationOnMock invocation) {
                    Object[] args = invocation.getArguments();
                    ByteBuffer buffer = (ByteBuffer) args[0];
                    long position = (Long) args[1];

                    if (position >= length) {
                        return -1;
                    }

                    int actualRead = (int) Math.min(buffer.remaining(), length - position);
                    buffer.position(buffer.position() + actualRead);
                    return actualRead;
                }
            });
            return this;
        }
    }

    /** A builder of EmbeddedReadRequest with a fluent interface. */
    private static class ReadRequestMaker {
        private final GenericReadRequestMessage.EmbeddedReadRequest _request;

        private ReadRequestMaker(GenericReadRequestMessage.EmbeddedReadRequest request) {
            _request = request;
        }

        public ReadRequestMaker forFileHandle(int fh) {
            when(_request.getFileHandle()).thenReturn(fh);
            return this;
        }

        public ReadRequestMaker forLength(int bytes) {
            when(_request.BytesToRead()).thenReturn(bytes);
            return this;
        }

        public ReadRequestMaker atOffset(long position) {
            when(_request.getOffset()).thenReturn(position);
            return this;
        }
    }
}