/*
 * Copyright 2016 Palantir Technologies, Inc. All rights reserved.
 */

package com.palantir.common.compression;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Random;

import org.junit.After;
import org.junit.Test;

import com.google.common.io.ByteStreams;

import net.jpountz.lz4.LZ4BlockInputStream;

public class LZ4CompressionTests {

    private static final byte SINGLE_VALUE = 42;
    private static final int BLOCK_SIZE = 1 << 16; // 64 KB

    private ByteArrayInputStream uncompressedStream;
    private LZ4CompressingInputStream compressingStream;
    private LZ4BlockInputStream decompressingStream;

    @After
    public void after() throws IOException {
        uncompressedStream.close();
        compressingStream.close();
        decompressingStream.close();
    }

    @Test
    public void testEmptyStream() throws IOException {
        initializeStreams(new byte[0]);
        assertStreamIsEmpty(decompressingStream);
    }

    @Test
    public void testSingleCharacterStream() throws IOException {
        byte[] uncompressedData = new byte[] { SINGLE_VALUE };
        initializeStreams(uncompressedData);
        verifyStreamContents(uncompressedData);
    }

    @Test
    public void testSingleCharacterSteam_singleByteRead() throws IOException {
        byte[] uncompressedData = new byte[] { SINGLE_VALUE };
        initializeStreams(uncompressedData);
        int value = decompressingStream.read();

        assertEquals(uncompressedData[0], value);
        assertStreamIsEmpty(decompressingStream);
    }

    @Test
    public void testSingleBlock() throws IOException {
        byte[] uncompressedData = new byte[BLOCK_SIZE];
        fillWithCompressibleData(uncompressedData);
        initializeStreams(uncompressedData);
        verifyStreamContents(uncompressedData);
    }

    @Test
    public void testSingleBlock_incompressible() throws IOException {
        byte[] uncompressedData = new byte[BLOCK_SIZE];
        fillWithIncompressibleData(uncompressedData);
        initializeStreams(uncompressedData);
        verifyStreamContents(uncompressedData);
    }

    @Test
    public void testMultiBlock_compressible() throws IOException {
        byte[] uncompressedData = new byte[16 * BLOCK_SIZE];
        fillWithCompressibleData(uncompressedData);
        initializeStreams(uncompressedData);
        verifyStreamContents(uncompressedData);
    }

    @Test
    public void testMultiBlock_incompressible() throws IOException {
        byte[] uncompressedData = new byte[16 * BLOCK_SIZE];
        fillWithIncompressibleData(uncompressedData);
        initializeStreams(uncompressedData);
        verifyStreamContents(uncompressedData);
    }

    @Test
    public void testMultiBlock_singleByteReads() throws IOException {
        byte[] uncompressedData = new byte[16 * BLOCK_SIZE];
        fillWithIncompressibleData(uncompressedData);
        initializeStreams(uncompressedData);

        for (int i = 0; i < uncompressedData.length; ++i) {
            int value = decompressingStream.read();
            assertEquals(Byte.toUnsignedInt(uncompressedData[i]), value);
        }
        assertStreamIsEmpty(decompressingStream);
    }

    @Test
    public void testMultiBlock_readPastEnd() throws IOException {
        byte[] uncompressedData = new byte[16 * BLOCK_SIZE];
        fillWithCompressibleData(uncompressedData);
        initializeStreams(uncompressedData);

        byte[] decompressedData = new byte[17 * BLOCK_SIZE];
        int bytesRead = ByteStreams.read(decompressingStream, decompressedData, 0, decompressedData.length);
        assertEquals(uncompressedData.length, bytesRead);
        assertArrayEquals(uncompressedData, Arrays.copyOf(decompressedData, bytesRead));
    }

    private void initializeStreams(byte[] uncompressedData) throws IOException {
        uncompressedStream = new ByteArrayInputStream(uncompressedData);
        compressingStream = new LZ4CompressingInputStream(uncompressedStream);
        decompressingStream = new LZ4BlockInputStream(compressingStream);
    }

    private void fillWithCompressibleData(byte[] data) {
        Arrays.fill(data, SINGLE_VALUE);
    }

    private void fillWithIncompressibleData(byte[] data) {
        new Random(0).nextBytes(data);
    }

    private void verifyStreamContents(byte[] uncompressedData) throws IOException {
        byte[] decompressedData = new byte[uncompressedData.length];
        ByteStreams.read(decompressingStream, decompressedData, 0, decompressedData.length);
        assertArrayEquals(uncompressedData, decompressedData);
        assertStreamIsEmpty(decompressingStream);
    }

    private void assertStreamIsEmpty(InputStream stream) throws IOException {
        assertEquals(-1, stream.read());
    }
}

@@ -1,10 +1,11 @@
 /*
+*
 %0A * Copy
@@ -40,34 +40,548 @@
 gies
-, Inc. All rights reserved
+%0A *%0A * Licensed under the BSD-3 License (the %22License%22);%0A * you may not use this file except in compliance with the License.%0A * You may obtain a copy of the License at%0A *%0A * http://opensource.org/licenses/BSD-3-Clause%0A *%0A * Unless required by applicable law or agreed to in writing, software%0A * distributed under the License is distributed on an %22AS IS%22 BASIS,%0A * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.%0A * See the License for the specific language governing permissions and%0A * limitations under the License
 .%0A *

Fix copyright notice