com.tinspx.util.io.ChannelSourceTest.java Source code

Java tutorial

Introduction

Here is the source code for com.tinspx.util.io.ChannelSourceTest.java

Source

/* Copyright (C) 2013-2014 Ian Teune <ian.teune@gmail.com>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
package com.tinspx.util.io;

import static com.google.common.base.Preconditions.*;
import static com.tinspx.util.io.ChannelSource.*;
import com.google.common.base.Charsets;
import com.google.common.base.Function;
import com.google.common.io.ByteSource;
import com.google.common.io.Closer;
import com.google.common.io.Files;
import com.google.common.io.Resources;
import java.io.Closeable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.util.Arrays;
import java.util.Random;
import javax.annotation.Nullable;
import lombok.NonNull;
import org.apache.commons.io.FileUtils;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;

/**
 *
 * @author Ian
 */
public class ChannelSourceTest {

    private static byte[] INPUT;
    private static byte[] EMPTY;

    public ChannelSourceTest() {
    }

    @BeforeClass
    public static void setUpClass() {
        INPUT = IOTest.getRandomBytes(new Random(), 1024 * 64, Charsets.UTF_8);
        EMPTY = new byte[INPUT.length];
    }

    @AfterClass
    public static void tearDownClass() {
        INPUT = null;
        EMPTY = null;
    }

    @Before
    public void setUp() {
    }

    @After
    public void tearDown() {
    }

    @Test
    public void testEmptySource() throws IOException {
        ByteSourceTests.assertEmpty(ChannelSource.empty());
        ByteSourceTests.assertEmpty(ByteSource.empty());
    }

    @Test
    public void testBAChannelSource() throws IOException {
        ChannelSource s = ChannelSource.of(INPUT);
        assertTrue(s.hasKnownSize());
        assertEquals(INPUT.length, s.size());
        ByteSourceTests.testByteSource(s, INPUT);

        int off = 443, len = 17167;
        s = ChannelSource.of(INPUT, off, len);
        assertTrue(s.hasKnownSize());
        assertEquals(len, s.size());
        ByteSourceTests.testByteSource(s, Arrays.copyOfRange(INPUT, off, off + len));
    }

    @Test
    public void testWrap() throws IOException {
        ByteSourceTests.testByteSource(ChannelSource.of(ByteSource.wrap(INPUT)), INPUT);
        ByteSourceTests.testByteSource(ChannelSource.of(Forced.byteSource(ChannelSource.of(INPUT))), INPUT);
        ByteSourceTests.testByteSource(
                ChannelSource.of(Forced.byteSource(new ByteSourceTests.ForcedByteArray(INPUT))), INPUT);
        ByteSourceTests.testByteSource(
                ChannelSource.of(Forced.byteSource(ChannelSource.of(ByteBuffer.wrap(INPUT)))), INPUT);
        ByteSourceTests.testByteSource(
                ChannelSource.of(Forced.byteSource(ChannelSource.of(ByteBuffer.wrap(INPUT).asReadOnlyBuffer()))),
                INPUT);
    }

    @Test
    public void testByteBufferSource() throws IOException {
        int off = 443, len = 17167;
        ByteBuffer buf, direct;
        direct = ByteBuffer.allocateDirect(INPUT.length);
        assertTrue(direct.isDirect());
        direct.put(INPUT);
        byte[] sub = Arrays.copyOfRange(INPUT, off, off + len);

        //full input
        buf = ByteBuffer.wrap(INPUT);
        ByteSourceTests.testByteSource(ChannelSource.of(buf), INPUT);
        assertEquals(0, buf.position());
        assertEquals(INPUT.length, buf.limit());

        buf = ByteBuffer.wrap(INPUT).asReadOnlyBuffer();
        ByteSourceTests.testByteSource(ChannelSource.of(buf), INPUT);
        assertEquals(0, buf.position());
        assertEquals(INPUT.length, buf.limit());

        direct.clear();
        buf = direct;
        ByteSourceTests.testByteSource(ChannelSource.of(buf), INPUT);
        assertEquals(0, buf.position());
        assertEquals(INPUT.length, buf.limit());

        //sub range of input
        buf = ByteBuffer.wrap(INPUT);
        buf.clear().position(off).limit(off + len);
        ByteSourceTests.testByteSource(ChannelSource.of(buf), sub);
        assertEquals(off, buf.position());
        assertEquals(off + len, buf.limit());

        buf = ByteBuffer.wrap(INPUT).asReadOnlyBuffer();
        buf.clear().position(off).limit(off + len);
        ByteSourceTests.testByteSource(ChannelSource.of(buf), sub);
        assertEquals(off, buf.position());
        assertEquals(off + len, buf.limit());

        direct.clear();
        buf = direct;
        buf.clear().position(off).limit(off + len);
        ByteSourceTests.testByteSource(ChannelSource.of(buf), sub);
        assertEquals(off, buf.position());
        assertEquals(off + len, buf.limit());
    }

    @Test
    public void testMemoizeSource() throws IOException {
        ByteSource source = of(ByteBuffer.allocate(16));
        assertSame(source, ChannelSource.memoize(source));

        source = of(new byte[16]);
        assertSame(source, ChannelSource.memoize(source));

        source = memoize(ByteSource.empty());
        assertSame(source, source);

        source = memoize(ByteSourceTests.force(of(INPUT)));
        assertTrue(source instanceof ChannelSource.MemoizeChannelSource);
        ByteSourceTests.testByteSource(source, INPUT);

        source = memoize(ByteSourceTests.force(of(ByteBuffer.wrap(INPUT))));
        assertTrue(source instanceof ChannelSource.MemoizeChannelSource);
        ByteSourceTests.testByteSource(source, INPUT);

        source = memoize(ByteSourceTests.force(of(ByteBuffer.wrap(INPUT).asReadOnlyBuffer())));
        assertTrue(source instanceof ChannelSource.MemoizeChannelSource);
        ByteSourceTests.testByteSource(source, INPUT);

        ByteBuffer direct = ByteBuffer.allocateDirect(INPUT.length);
        assertTrue(direct.isDirect());
        direct.put(INPUT).flip();
        source = memoize(ByteSourceTests.force(of(direct)));
        assertTrue(source instanceof ChannelSource.MemoizeChannelSource);
        ByteSourceTests.testByteSource(source, INPUT);

        source = memoize(new BAOutputStream(INPUT, INPUT.length).asByteSource());
        assertTrue(source instanceof ChannelSource.MemoizeChannelSource);
        ByteSourceTests.testByteSource(source, INPUT);

        source = memoize(new ByteSourceTests.ForcedByteArray(INPUT));
        assertTrue(source instanceof ChannelSource.MemoizeChannelSource);
        ByteSourceTests.testByteSource(source, INPUT);

        source = memoize(ByteSource.wrap(INPUT));
        assertTrue(source instanceof ChannelSource.MemoizeChannelSource);
        ByteSourceTests.testByteSource(source, INPUT);

        ByteSourceTests.assertEmpty(memoize(ByteSource.empty()));
        ByteSourceTests.assertEmpty(memoize(ChannelSource.empty()));
    }

    static final String FORMATTED = "formatted.txt";
    static final String RAW = "raw.txt";
    static final String SAMPLE = "json_sample.txt";

    static File OUTPUT;

    static File outputFile() {
        if (OUTPUT == null) {
            OUTPUT = new File(new File(System.getProperty("user.dir")), "test/output.txt");
        }
        return OUTPUT;
    }

    /**
     * checks the output file against {@code ref}, then deletes it
     */
    @SuppressWarnings("ThrowFromFinallyBlock")
    static void checkOutputFile(byte[] ref) throws IOException {
        try {
            assertArrayEquals(ref, ByteUtils.toByteArray(outputFile()));
        } finally {
            if (!outputFile().delete()) {
                throw new IOException("could not delete: " + outputFile());
            }
        }
    }

    static void deleteOutputFile() {
        if (outputFile().exists()) {
            if (!outputFile().delete()) {
                throw new RuntimeException("could not delete: " + outputFile());
            }
        }
    }

    static File setupTestDir() throws IOException {
        File testDir = new File(new File(System.getProperty("user.dir")), "test");
        if (testDir.exists()) {
            if (testDir.list().length > 0) {
                throw new IOException(testDir + " exists and is not empty");
            }
        } else if (!testDir.mkdirs()) {
            throw new IOException("could not create " + testDir);
        }
        boolean complete = false;
        try {
            Resources.asByteSource(ChannelSource.class.getResource("/" + FORMATTED))
                    .copyTo(Files.asByteSink(new File(testDir, FORMATTED)));
            Resources.asByteSource(ChannelSource.class.getResource("/" + SAMPLE))
                    .copyTo(Files.asByteSink(new File(testDir, SAMPLE)));
            complete = true;
            return testDir;
        } finally {
            if (!complete) {
                teardownTestDir(testDir).close();
            }
        }
    }

    static Closeable teardownTestDir(@NonNull final File testDir) throws IOException {
        return new Closeable() {
            @Override
            public void close() throws IOException {
                FileUtils.deleteDirectory(testDir);
            }
        };
    }

    @Test
    public void testFileSource() throws IOException {
        File testDir = setupTestDir();
        Closer closer = Closer.create();
        try {
            closer.register(teardownTestDir(testDir));
            testFileSource(new File(testDir, FORMATTED));
            testFileSource(new File(testDir, SAMPLE));
        } catch (Throwable t) {
            throw closer.rethrow(t);
        } finally {
            closer.close();
        }
    }

    static void testFileSource(File file) throws IOException {
        System.out.printf("Testing %s; Length: %s%n", file, file.length());
        byte[] ref = ByteUtils.toByteArray(file);

        testFileSource(ref, file, null);
        testFileSource(ref, file, fromRandomAccessFile());
        testFileSource(ref, file, fromRandomAccessFile("r"));
        testFileSource(ref, file, fromRandomAccessFile("rws"));
        testFileSource(ref, file, fromRandomAccessFile("rwd"));
    }

    static void checkOutputFileDNE() {
        checkState(!outputFile().exists(), "(%s) exists", outputFile());
    }

    static void testFileSource(byte[] ref, File file, @Nullable Function<File, FileChannel> function)
            throws IOException {
        Closer closer;
        ChannelSource s;
        if (function != null) {
            s = of(file, function);
        } else {
            s = of(file);
        }
        ByteSourceTests.testByteSource(s, ref);
        assertTrue(s.hasKnownSize());
        assertEquals(file.length(), s.size());

        //normal FileOutputStream
        checkOutputFileDNE();
        closer = Closer.create();
        try {
            assertEquals(ref.length, s.copyTo(closer.register(new FileOutputStream(outputFile()))));
        } catch (Throwable t) {
            throw closer.rethrow(t);
        } finally {
            closer.close();
        }
        checkOutputFile(ref);

        //force FileOutputStream as OutputStream
        checkOutputFileDNE();
        closer = Closer.create();
        try {
            assertEquals(ref.length,
                    s.copyTo(closer.register(Forced.outputStream(new FileOutputStream(outputFile())))));
        } catch (Throwable t) {
            throw closer.rethrow(t);
        } finally {
            closer.close();
        }
        checkOutputFile(ref);

        //copyTo a WritableByteChannel
        checkOutputFileDNE();
        closer = Closer.create();
        try {
            //ByteUtils.asWritableByteChannel extends OutputStream (actually turned out to be usefull)
            OutputStream out = (OutputStream) ByteUtils
                    .asWritableByteChannel(closer.register(new FileOutputStream(outputFile())));
            assert (out instanceof ByteUtils.OutputStreamWritableByteChannel);
            assertEquals(ref.length, s.copyTo(out));
        } catch (Throwable t) {
            throw closer.rethrow(t);
        } finally {
            closer.close();
        }
        checkOutputFile(ref);

        checkOutputFileDNE();
        assertEquals(ref.length, s.copyTo(ChannelSink.of(outputFile())));
        checkOutputFile(ref);

        checkOutputFileDNE();
        assertEquals(ref.length, s.copyTo(ChannelSink.of(outputFile(), fromRandomAccessFile())));
        checkOutputFile(ref);

        checkOutputFileDNE();
        assertEquals(ref.length, s.copyTo(ChannelSink.of(outputFile(), fromRandomAccessFile("rws"))));
        checkOutputFile(ref);

        checkOutputFileDNE();
        assertEquals(ref.length, s.copyTo(ChannelSink.of(outputFile(), fromRandomAccessFile("rwd"))));
        checkOutputFile(ref);

        //test weird channel and sink
        BAOutputStream out = new BAOutputStream(ref.length);
        assertEquals(ref.length, ((ChannelSource.FileChannelSource) s).copyTo(new WeirdWritableChannel(out)));
        assertArrayEquals(ref, out.backingToByteArray());

        out.clearAndReset();
        assertEquals(ref.length, s.copyTo(new WeirdChannelSink(out.asByteSink())));
        assertArrayEquals(ref, out.backingToByteArray());
    }

    static class WeirdChannelSink extends ChannelSink {
        final ChannelSink delegate;

        public WeirdChannelSink(ChannelSink delegate) {
            this.delegate = checkNotNull(delegate);
        }

        @Override
        public WritableByteChannel openChannel() throws IOException {
            return new WeirdWritableChannel(delegate.openChannel());
        }
    }

    static class WeirdWritableChannel implements WritableByteChannel {

        final WritableByteChannel delegate;
        int count;

        public WeirdWritableChannel(WritableByteChannel delegate) {
            this.delegate = checkNotNull(delegate);
        }

        @SuppressWarnings("fallthrough")
        @Override
        public int write(ByteBuffer dst) throws IOException {
            switch (count++ % 5) {
            case 0:
            case 3: //don't allow writing
                return 0;

            case 1:
            case 2: //only allow half of dst to be used
                if (dst.remaining() > 1) {
                    final int limit = dst.limit();
                    dst.limit(dst.position() + (dst.remaining() / 2));
                    int w = delegate.write(dst);
                    dst.limit(limit);
                    return w;
                }
                //fall through
            case 4: //normal write
                return delegate.write(dst);

            default:
                throw new AssertionError();
            }
        }

        @Override
        public boolean isOpen() {
            return delegate.isOpen();
        }

        @Override
        public void close() throws IOException {
            delegate.close();
        }
    }
}