package com.atlassian.bitbucket.scm.cache.util;

import org.apache.commons.io.IOUtils;
import org.junit.Before;
import org.junit.Test;

import java.io.ByteArrayInputStream;
import java.io.IOException;

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

public class ResettableInputStreamTest {

    private static final String DATA = "0123456789abcdefghijklmnopqursuvwxyz";

    private ResettableInputStream inputStream;

    @Before
    public void setup() throws Exception {
        inputStream = new ResettableInputStream(new ByteArrayInputStream(DATA.getBytes("UTF-8")));
    }

    @Test
    public void testAvailable() throws IOException {
        readBytes(5);

        assertEquals(31, inputStream.available());
        inputStream.mark();
        assertEquals(31, inputStream.available());
        readBytes(5);
        assertEquals(26, inputStream.available());
        inputStream.reset();
        assertEquals(31, inputStream.available());
        readBytes(5);
        assertEquals(26, inputStream.available());
    }

    @Test
    public void testBasicRead() throws IOException {
        assertEquals(DATA, IOUtils.toString(inputStream, "UTF-8"));
    }

    @Test
    public void testClearMark() throws IOException {
        readBytes(5);
        inputStream.mark();
        readBytes(5);
        inputStream.clearMark();
        assertEquals(DATA.substring(10), IOUtils.toString(inputStream));
    }

    @Test
    public void testMarkResetFullString() throws IOException {
        inputStream.mark();
        assertEquals(DATA, IOUtils.toString(inputStream, "UTF-8"));
        inputStream.reset();
        assertEquals(DATA, IOUtils.toString(inputStream, "UTF-8"));
    }

    @Test
    public void testMarkResetSubString() throws IOException {
        // skip 2 bytes
        readBytes(2);

        inputStream.mark();
        readBytes(5);
        inputStream.reset();

        assertEquals(DATA.substring(2), IOUtils.toString(inputStream));
    }

    @Test
    public void testMarkResetMultipleTimes() throws IOException {
        readBytes(5);

        inputStream.mark();
        readBytes(4);
        inputStream.reset();

        readBytes(2);
        inputStream.mark();

        assertEquals(DATA.substring(7), IOUtils.toString(inputStream));
        inputStream.reset();
        assertEquals(DATA.substring(7), IOUtils.toString(inputStream));
    }

    @Test
    public void testMarkSupported() {
        assertTrue(inputStream.markSupported());
    }

    @Test(expected = IOException.class)
    public void testResetWithoutMark() throws IOException {
        inputStream.reset();
    }

    @Test
    public void testSkip() throws IOException {
        inputStream.skip(5);
        inputStream.mark();
        inputStream.skip(5);
        inputStream.reset();
        assertEquals(DATA.substring(5), IOUtils.toString(inputStream));
    }

    @Test(expected = IOException.class)
    public void testResetClosed() throws IOException {
        inputStream.mark();
        readBytes(5);
        inputStream.close();
        inputStream.mark();
        inputStream.reset();
    }

    private void readBytes(int count) throws IOException {
        for (int i = 0; i < count; ++i) {
            inputStream.read();
        }
    }
}
