XnioFileChannelTestCase.java

/*
 * JBoss, Home of Professional Open Source.
 *
 * Copyright 2013 Red Hat, Inc. and/or its affiliates, and individual
 * contributors as indicated by the @author tags.
 *
 * 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 org.xnio;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.xnio.AssertReadWrite.assertReadMessage;
import static org.xnio.AssertReadWrite.assertWrittenMessage;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.nio.channels.FileLock;
import java.nio.channels.OverlappingFileLockException;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.xnio.channels.BlockingByteChannel;
import org.xnio.channels.PushBackStreamChannel;
import org.xnio.mock.ConnectedStreamChannelMock;

/**
 * Test for {@link XnioFileChannel}.
 * 
 * @author <a href="mailto:frainone@redhat.com">Flavia Rainone</a>
 *
 */
public class XnioFileChannelTestCase {

    private RandomAccessFile randomAccessFile;
    private FileChannel fileChannel;

    @Before
    public void initFileChannel() throws IOException {
        final File file = File.createTempFile("test", ".txt");
        file.deleteOnExit();
        randomAccessFile = new RandomAccessFile(file, "rw");
        fileChannel = new XnioFileChannel(randomAccessFile.getChannel());
    }

    @After
    public void closeFileChannel() throws IOException {
        fileChannel.close();
        randomAccessFile.close();
    }

    @Test
    public void simpleReadAndWrite() throws IOException {
        assertEquals(0, fileChannel.size());

        final ByteBuffer buffer = ByteBuffer.allocate(5);
        buffer.put("abcde".getBytes()).flip();
        assertEquals(5, fileChannel.write(buffer));

        assertEquals(5, fileChannel.size());
        assertEquals(5, fileChannel.position());
        fileChannel.position(0);
        assertEquals(0, fileChannel.position());
        assertEquals(5, fileChannel.size());

        final ByteBuffer readBuffer = ByteBuffer.allocate(10);
        assertEquals(5, fileChannel.read(readBuffer));
        assertEquals(5, fileChannel.position());
        assertReadMessage(readBuffer, "abcde");
        assertEquals(5, fileChannel.size());
    }

    @Test
    public void readAndWriteWithOffset() throws IOException {
        final ByteBuffer[] buffers = new ByteBuffer[] {ByteBuffer.allocate(3), ByteBuffer.allocate(3),
                ByteBuffer.allocate(3), ByteBuffer.allocate(3), ByteBuffer.allocate(3), ByteBuffer.allocate(3)};
        buffers[0].put("#$%".getBytes()).flip();
        buffers[1].put("abc".getBytes()).flip();
        buffers[2].put("de".getBytes()).flip();
        buffers[3].flip();
        buffers[4].put("ABC".getBytes()).flip();
        buffers[5].put("DEF".getBytes()).flip();
        assertEquals(5, fileChannel.write(buffers, 1, 3));

        assertEquals(5, fileChannel.position());
        fileChannel.position(0);
        assertEquals(0, fileChannel.position());

        final ByteBuffer[] readBuffers = new ByteBuffer[] {ByteBuffer.allocate(1), ByteBuffer.allocate(2),
                ByteBuffer.allocate(3), ByteBuffer.allocate(4)};
        assertEquals(2, fileChannel.read(readBuffers, 1, 1));
        assertEquals(2, fileChannel.position());
        assertReadMessage(readBuffers[1], "ab");

        assertEquals(3, fileChannel.read(readBuffers, 1, 3));
        assertEquals(5, fileChannel.position());
        assertReadMessage(readBuffers[2], "cde");
    }

    @Test
    public void readAndWriteWithPosition() throws IOException {
        assertEquals(0, fileChannel.size());

        final ByteBuffer buffer = ByteBuffer.allocate(5);
        buffer.put("abcde".getBytes()).flip();
        assertEquals(5, fileChannel.write(buffer, 3));

        assertEquals(8, fileChannel.size());
        assertEquals(0, fileChannel.position());
        fileChannel.position(3);
        assertEquals(3, fileChannel.position());
        assertEquals(8, fileChannel.size());

        final ByteBuffer readBuffer = ByteBuffer.allocate(10);
        assertEquals(4, fileChannel.read(readBuffer, 4));
        assertEquals(3, fileChannel.position());
        assertReadMessage(readBuffer, "bcde");
        assertEquals(8, fileChannel.size());
    }

    @Test
    public void truncate() throws IOException {
        assertEquals(0, fileChannel.size());

        final ByteBuffer buffer = ByteBuffer.allocate(20);
        buffer.put("message to truncate".getBytes()).flip();
        assertEquals(19, fileChannel.write(buffer));
        assertEquals(19, fileChannel.size());

        fileChannel.truncate(10);
        assertEquals(10, fileChannel.size());

        buffer.clear();
        fileChannel.position(0);
        assertEquals(10, fileChannel.read(buffer));
        assertReadMessage(buffer, "message to");

        buffer.clear();
        fileChannel.truncate(15);
        assertEquals(10, fileChannel.size());

        buffer.clear();
        fileChannel.position(0);
        assertEquals(10, fileChannel.read(buffer));
        assertReadMessage(buffer, "message to");

        buffer.clear();
        fileChannel.truncate(7);
        assertEquals(7, fileChannel.size());

        buffer.clear();
        fileChannel.position(0);
        assertEquals(7, fileChannel.read(buffer));
        assertReadMessage(buffer, "message");
        
        //test force... nothing noticeable should happen
        fileChannel.force(true);
        fileChannel.force(false);
    }

    @Test
    public void transferBlockingToFile1() throws IOException {
        final ConnectedStreamChannelMock channelMock = new ConnectedStreamChannelMock();
        channelMock.setReadData("test");
        channelMock.enableRead(true);
        fileChannel.transferFrom(channelMock, 0, 4);
        fileChannel.position(0);
        ByteBuffer buffer = ByteBuffer.allocate(10);
        fileChannel.read(buffer);
        assertReadMessage(buffer, "test");
    }

    @Test
    public void transferBlockingToFile2() throws IOException, InterruptedException {
        final ConnectedStreamChannelMock channelMock = new ConnectedStreamChannelMock();
        channelMock.setReadData("test", "12345");
        channelMock.enableRead(true);
        fileChannel.transferFrom(new PushBackStreamChannel(channelMock), 0, 8);
        fileChannel.position(0);
        ByteBuffer buffer = ByteBuffer.allocate(10);
        fileChannel.read(buffer);
        assertReadMessage(buffer, "test", "1234");
    }

    @Test
    public void transferBlockingToFile3() throws IOException {
        final ConnectedStreamChannelMock channelMock = new ConnectedStreamChannelMock();
        channelMock.setReadData("test");
        channelMock.enableRead(true);
        fileChannel.transferFrom(new BlockingByteChannel(channelMock), 0, 4);
        fileChannel.position(0);
        ByteBuffer buffer = ByteBuffer.allocate(10);
        fileChannel.read(buffer);
        assertReadMessage(buffer, "test");
    }

    @Test
    public void transferBlockingFromFile1() throws IOException {
        final ConnectedStreamChannelMock channelMock = new ConnectedStreamChannelMock();
        final ByteBuffer buffer = ByteBuffer.allocate(10);
        buffer.put("test".getBytes("UTF-8")).flip();
        assertEquals(4, fileChannel.write(buffer));
        fileChannel.position(0);
        fileChannel.transferTo(0, 4, channelMock);
        assertWrittenMessage(channelMock, "test");
    }

    @Test
    public void transferBlockingFromFile2() throws IOException, InterruptedException {
        final ConnectedStreamChannelMock channelMock = new ConnectedStreamChannelMock();
        channelMock.enableWrite(false);
        final ByteBuffer buffer = ByteBuffer.allocate(10);
        buffer.put("test12345".getBytes("UTF-8")).flip();
        assertEquals(9, fileChannel.write(buffer));
        fileChannel.position(0);

        channelMock.enableWrite(true);
        fileChannel.transferTo(0, 8, channelMock);
        assertWrittenMessage(channelMock, "test", "1234");
    }

    @Test
    public void transferBlockingFromFile3() throws IOException {
        final ConnectedStreamChannelMock channelMock = new ConnectedStreamChannelMock();
        final ByteBuffer buffer = ByteBuffer.allocate(10);
        buffer.put("test".getBytes("UTF-8")).flip();
        assertEquals(4, fileChannel.write(buffer));
        fileChannel.position(0);
        fileChannel.transferTo(0, 4, new BlockingByteChannel(channelMock));
        assertWrittenMessage(channelMock, "test");
    }

    @Test
    public void map() throws IOException {
        final ByteBuffer buffer = ByteBuffer.allocate(10);
        buffer.put("1234567890".getBytes()).flip();
        assertEquals(10, fileChannel.write(buffer));

        MappedByteBuffer mappedBuffer = fileChannel.map(MapMode.READ_WRITE, 5, 2);
        assertNotNull(mappedBuffer);
        assertEquals(2, mappedBuffer.limit());
        mappedBuffer.position(2);
        assertReadMessage(mappedBuffer, "67");
    }

    @Test
    public void lock() throws IOException {
        final ByteBuffer buffer = ByteBuffer.allocate(10);
        buffer.put("1234567890".getBytes()).flip();
        assertEquals(10, fileChannel.write(buffer));

        FileLock lock = fileChannel.lock();
        assertNotNull(lock);

        OverlappingFileLockException expected = null;
        try {
            fileChannel.lock(3, 5, false);
        } catch (OverlappingFileLockException e) {
            expected = e;
        }
        assertNotNull(expected);
        lock.release();

        lock = fileChannel.lock(3, 5, false);
        assertNotNull(lock);

        expected = null;
        try {
            fileChannel.tryLock();
        } catch (OverlappingFileLockException e) {
            expected = e;
        }
        assertNotNull(expected);
        
        lock.release();
        lock = fileChannel.tryLock();
        assertNotNull(lock);
        lock.release();

        lock = fileChannel.tryLock(1, 3, true);
        assertNotNull(lock);

        expected = null;
        try {
            fileChannel.tryLock(1, 3, true);
        } catch (OverlappingFileLockException e) {
            expected = e;
        }
        assertNotNull(expected);
    }
}