package com.avg.zaap.crashlibrary.core.writer;

import android.annotation.SuppressLint;

import org.junit.Before;
import org.junit.Test;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

/**
 * A test class for {@link DataWriter}.
 * <p>
 * Created by marek.osvald on 2.5.2016.
 */
public class DataWriterTests {

    @SuppressLint("SdCardPath")
    private static final String PATH = "/data/data/app/foo.txt";
    private static final String DATA = "data";

    private IDataWriter mDataWriter;
    private File mFile;
    private Writer mWriter;
    private Writer mBufferedWriter;
    private Result mFileWriterResult;

    @Before
    public void setUp() throws Exception {
        mFile = mock(File.class);
        mWriter = mock(FileWriter.class);
        mBufferedWriter = mock(BufferedWriter.class);

        mDataWriter = new DataWriter(new IDataWriterFactory() {
            @Override
            public File getFile(String path) {
                assertThat(path, is(PATH));
                return mFile;
            }

            @Override
            public Writer getFileWriter(File file) throws IOException {
                assertThat(file, sameInstance(mFile));

                switch (mFileWriterResult) {
                    case OK:
                        return mWriter;

                    case EXCEPTION:
                        throw new IOException();

                    default:
                        fail();
                        return null;
                }
            }

            @Override
            public Writer getBufferedWriter(Writer writer) {
                assertThat(writer, sameInstance(mWriter));
                return mBufferedWriter;
            }
        });

        initializeWriter(Result.OK);
        mFileWriterResult = Result.OK;
    }

    @Test
    public void write_dataEmpty_doesNotWrite() throws Exception {
        // act
        mDataWriter.write(PATH, "");

        // assert
        verify(mBufferedWriter, never()).write(anyString());
    }

    @Test
    public void write_dataNotEmpty_writesDataClosesStream() throws Exception {
        // act
        mDataWriter.write(PATH, DATA);

        // assert
        verify(mBufferedWriter, times(1)).write(DATA);
        verify(mBufferedWriter, times(1)).close();
    }

    @Test
    public void write_dataNotEmptyFileWriterCrashes_throwsException() throws Exception {
        // arrange
        mFileWriterResult = Result.EXCEPTION;

        // act
        try {
            mDataWriter.write(PATH, DATA);
            fail();
        } catch (DataWriterException ex) { /* ignored */ }
    }

    @Test
     public void write_dataNotEmptyBufferedWriterCrashes_throwsException() throws Exception {
        // arrange
        initializeWriter(Result.EXCEPTION);

        // act
        try {
            mDataWriter.write(PATH, DATA);
        } catch (DataWriterException ex) { /* ignored */ }

        // assert
        verify(mBufferedWriter, times(1)).write(DATA);
        verify(mBufferedWriter, times(1)).close();
    }

    @Test
    public void write_dataNotEmptyBufferedWriterCrashesCloseThrowsException_throwsException() throws Exception {
        // arrange
        doThrow(new IOException()).when(mBufferedWriter).close();

        // act
        try {
            mDataWriter.write(PATH, DATA);
        } catch (DataWriterException ex) { /* ignored */ }

        // assert
        verify(mBufferedWriter, times(1)).write(DATA);
        verify(mBufferedWriter, times(1)).close();
    }

    private void initializeWriter(Result result) throws Exception {
        switch (result) {
            case OK:
                // doesn't really do anything, just in case its ever needed
                doNothing().when(mBufferedWriter).write(DATA);
                break;

            case EXCEPTION:
                doThrow(new IOException()).when(mBufferedWriter).write(DATA);
                break;

            default:
                fail();
        }
    }

    enum Result {
        OK, EXCEPTION
    }

}
