package com.avg.zaap.crashlibrary;

import android.os.Parcel;

import org.junit.Before;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsSame.sameInstance;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;

/**
 * A test class for {@link ReportData}.
 * <p>
 * Created by marek.osvald on 18.7.2016.
 */
public class ReportDataTests {

    private static final int NO_MASK = 0;
    private static final int MOCK_FLAGS = 1;

    private static final String MOCK_CRASH_CODE = "mockCrashCode";
    private static final String MOCK_ERROR_NAME = "mockErrorName";
    private static final String MOCK_ERROR_MESSAGE = "mockErrorMessage";
    private static final String MOCK_ERROR_DETAILS = "mockErrorDetails";
    private static final int MOCK_FILES_TO_PACK_SIZE = 3;
    private static final String MOCK_FILE_TO_PACK_1 = "mockFileToPack1";
    private static final String MOCK_FILE_TO_PACK_2 = "mockFileToPack2";
    private static final String MOCK_FILE_TO_PACK_3 = "mockFileToPack3";

    private static final String MOCK_CAP_EXCEPTION_CODE = "mockCapExceptionCode";
    private static final int MOCK_CAP_FAULTING_OFFSET = 123;
    private static final String MOCK_CAP_MODULE_NAME = "mockModuleName";
    private static final String MOCK_CAP_MODULE_VERSION = "mockModuleVersion";
    private static final String MOCK_CAP_PROCESS_NAME = "mockProcessName";
    private static final String MOCK_CAP_PROCESS_VERSION = "mockProcessVersion";
    private static final String MOCK_CAP_GUID = "mockGUID";
    private static final int MOCK_PUBLISH_ATTEMPTS = 1;
    private static final long MOCK_LAST_ATTEMPT_TIMESTAMP = 1;

    private ReportData mReportData;
    private Parcel mParcel;
    private List<String> mFiles;

    @Before
    public void setUp() throws Exception {
        mReportData = new ReportData();

        mFiles = new ArrayList<>();
        mFiles.add(MOCK_FILE_TO_PACK_1);
        mFiles.add(MOCK_FILE_TO_PACK_2);
        mFiles.add(MOCK_FILE_TO_PACK_3);

        // common
        mReportData.setCrashCode(MOCK_CRASH_CODE);
        mReportData.setErrorName(MOCK_ERROR_NAME);
        mReportData.setErrorMessage(MOCK_ERROR_MESSAGE);
        mReportData.setErrorDetails(MOCK_ERROR_DETAILS);
        mReportData.setFilesToPack(mFiles);

        // CAP
        mReportData.setCapExceptionCode(MOCK_CAP_EXCEPTION_CODE);
        mReportData.setCapFaultingOffset(MOCK_CAP_FAULTING_OFFSET);
        mReportData.setCapModuleName(MOCK_CAP_MODULE_NAME);
        mReportData.setCapModuleVersion(MOCK_CAP_MODULE_VERSION);
        mReportData.setCapProcessName(MOCK_CAP_PROCESS_NAME);
        mReportData.setCapProcessVersion(MOCK_CAP_PROCESS_VERSION);
        mReportData.setCapGUID(MOCK_CAP_GUID);

        // offline
        mReportData.setPublishAttempts(MOCK_PUBLISH_ATTEMPTS);
        mReportData.setLastAttemptTimestamp(MOCK_LAST_ATTEMPT_TIMESTAMP);

        mParcel = mock(Parcel.class);
    }

    @Test
    public void describeContents_nothing_returnNoMask() throws Exception {
        // act
        final int result = mReportData.describeContents();

        // assert
        assertThat(result, is(NO_MASK));
    }

    @Test
    public void writeToParcel_nothing_writesDataToParcel() throws Exception {
        // act
        mReportData.writeToParcel(mParcel, MOCK_FLAGS);

        // assert
        // common
        verify(mParcel, times(1)).writeString(MOCK_CRASH_CODE);
        verify(mParcel, times(1)).writeString(MOCK_ERROR_NAME);
        verify(mParcel, times(1)).writeString(MOCK_ERROR_MESSAGE);
        verify(mParcel, times(1)).writeString(MOCK_ERROR_DETAILS);
        verify(mParcel, times(1)).writeStringList(mFiles);

        // CAP
        verify(mParcel, times(1)).writeString(MOCK_CAP_EXCEPTION_CODE);
        verify(mParcel, times(1)).writeInt(MOCK_CAP_FAULTING_OFFSET);
        verify(mParcel, times(1)).writeString(MOCK_CAP_MODULE_NAME);
        verify(mParcel, times(1)).writeString(MOCK_CAP_MODULE_VERSION);
        verify(mParcel, times(1)).writeString(MOCK_CAP_PROCESS_NAME);
        verify(mParcel, times(1)).writeString(MOCK_CAP_PROCESS_VERSION);
        verify(mParcel, times(1)).writeString(MOCK_CAP_GUID);

        // offline
        verify(mParcel, times(1)).writeInt(MOCK_PUBLISH_ATTEMPTS);
        verify(mParcel, times(1)).writeLong(MOCK_LAST_ATTEMPT_TIMESTAMP);

        verifyNoMoreInteractions(mParcel);
    }

    @Test
    public void constructor_parcel_readsValuesFromParcel() throws Exception {
        // arrange
        when(mParcel.readString()).thenReturn(
                MOCK_CRASH_CODE,
                MOCK_ERROR_NAME,
                MOCK_ERROR_MESSAGE,
                MOCK_ERROR_DETAILS,
                MOCK_CAP_EXCEPTION_CODE,
                MOCK_CAP_MODULE_NAME,
                MOCK_CAP_MODULE_VERSION,
                MOCK_CAP_PROCESS_NAME,
                MOCK_CAP_PROCESS_VERSION,
                MOCK_CAP_GUID
        );

        when(mParcel.readInt()).thenReturn(
            MOCK_CAP_FAULTING_OFFSET,
            MOCK_PUBLISH_ATTEMPTS
        );

        when(mParcel.readLong()).thenReturn(
                MOCK_LAST_ATTEMPT_TIMESTAMP
        );

        //noinspection unchecked
        doAnswer(new Answer<Void>() {
            @Override
            public Void answer(InvocationOnMock invocation) throws Throwable {
                //noinspection unchecked
                List<String> list = (List<String>) invocation.getArguments()[0];
                list.addAll(mFiles);
                return null;
            }
        }).when(mParcel).readStringList(any(List.class));

        // act
        ReportData reportData = new ReportData(mParcel);

        // assert
        // common
        assertThat(reportData.getCrashCode(), is(MOCK_CRASH_CODE));
        assertThat(reportData.getErrorName(), is(MOCK_ERROR_NAME));
        assertThat(reportData.getErrorMessage(), is(MOCK_ERROR_MESSAGE));
        assertThat(reportData.getErrorDetails(), is(MOCK_ERROR_DETAILS));
        assertThat(reportData.getFilesToPack().size(), is(MOCK_FILES_TO_PACK_SIZE));
        assertThat(reportData.getFilesToPack().contains(MOCK_FILE_TO_PACK_1), is(true));
        assertThat(reportData.getFilesToPack().contains(MOCK_FILE_TO_PACK_2), is(true));
        assertThat(reportData.getFilesToPack().contains(MOCK_FILE_TO_PACK_3), is(true));

        // CAP
        assertThat(reportData.getCapExceptionCode(), is(MOCK_CAP_EXCEPTION_CODE));
        assertThat(reportData.getCapFaultingOffset(), is(MOCK_CAP_FAULTING_OFFSET));
        assertThat(reportData.getCapModuleName(), is(MOCK_CAP_MODULE_NAME));
        assertThat(reportData.getCapModuleVersion(), is(MOCK_CAP_MODULE_VERSION));
        assertThat(reportData.getCapProcessName(), is(MOCK_CAP_PROCESS_NAME));
        assertThat(reportData.getCapProcessVersion(), is(MOCK_CAP_PROCESS_VERSION));
        assertThat(reportData.getCapGUID(), is(MOCK_CAP_GUID));

        // offline
        assertThat(reportData.getPublishAttempts(), is(MOCK_PUBLISH_ATTEMPTS));
        assertThat(reportData.getLastAttemptTimestamp(), is(MOCK_LAST_ATTEMPT_TIMESTAMP));
    }

    @Test
    public void getCrashCode_nothing_returnsCrashCode() throws Exception {
        // act
        final String result = mReportData.getCrashCode();

        // assert
        assertThat(result, is(MOCK_CRASH_CODE));
    }

    @Test
    public void setCrashCode_nothing_setsNewValue() throws Exception {
        // arrange
        assertThat(mReportData.getCrashCode(), is(MOCK_CRASH_CODE));

        // act
        final String newValue = "newValue";
        mReportData.setCrashCode(newValue);

        // assert
        assertThat(mReportData.getCrashCode(), is(newValue));
    }

    @Test
    public void getErrorMessage_nothing_returnsErrorMessage() throws Exception {
        // act
        final String result = mReportData.getErrorMessage();

        // assert
        assertThat(result, is(MOCK_ERROR_MESSAGE));
    }

    @Test
    public void setErrorMessage_nothing_setsNewValue() throws Exception {
        // arrange
        assertThat(mReportData.getErrorMessage(), is(MOCK_ERROR_MESSAGE));

        // act
        final String newValue = "newValue";
        mReportData.setErrorMessage(newValue);

        // assert
        assertThat(mReportData.getErrorMessage(), is(newValue));
    }

    @Test
    public void getErrorDetails_nothing_returnsErrorDetails() throws Exception {
        // act
        final String result = mReportData.getErrorDetails();

        // assert
        assertThat(result, is(MOCK_ERROR_DETAILS));
    }

    @Test
    public void setErrorDetails_nothing_setsNewValue() throws Exception {
        // arrange
        assertThat(mReportData.getErrorDetails(), is(MOCK_ERROR_DETAILS));

        // act
        final String newValue = "newValue";
        mReportData.setErrorDetails(newValue);

        // assert
        assertThat(mReportData.getErrorDetails(), is(newValue));
    }

    @Test
    public void getFilesToPack_nothing_returnsFilesToPack() throws Exception {
        // act
        final List<String> result = mReportData.getFilesToPack();

        // assert
        assertThat(result, sameInstance(mFiles));
    }

    @Test
    public void setFilesToPack_nothing_setsNewValue() throws Exception {
        // arrange
        assertThat(mReportData.getFilesToPack(), sameInstance(mFiles));

        // act
        final List<String> newValue = new LinkedList<>();
        mReportData.setFilesToPack(newValue);

        // assert
        assertThat(mReportData.getFilesToPack(), sameInstance(newValue));
    }

    @Test
    public void getCapExceptionCode_nothing_returnsCapExceptionCode() throws Exception {
        // act
        final String result = mReportData.getCapExceptionCode();

        // assert
        assertThat(result, is(MOCK_CAP_EXCEPTION_CODE));
    }

    @Test
    public void setCapExceptionCode_nothing_setsNewValue() throws Exception {
        // arrange
        assertThat(mReportData.getCapExceptionCode(), is(MOCK_CAP_EXCEPTION_CODE));

        // act
        final String newValue = "newValue";
        mReportData.setCapExceptionCode(newValue);

        // assert
        assertThat(mReportData.getCapExceptionCode(), is(newValue));
    }

    @Test
    public void getCapFaultingOffset_nothing_returnsCapFaultingOffset() throws Exception {
        // act
        final int result = mReportData.getCapFaultingOffset();

        // assert
        assertThat(result, is(MOCK_CAP_FAULTING_OFFSET));
    }

    @Test
    public void setCapFaultingOffset_nothing_setsNewValue() throws Exception {
        // arrange
        assertThat(mReportData.getCapFaultingOffset(), is(MOCK_CAP_FAULTING_OFFSET));

        // act
        final int newValue = 42;
        mReportData.setCapFaultingOffset(newValue);

        // assert
        assertThat(mReportData.getCapFaultingOffset(), is(newValue));
    }

    @Test
    public void getCapModuleName_nothing_returnsCapModuleName() throws Exception {
        // act
        final String result = mReportData.getCapModuleName();

        // assert
        assertThat(result, is(MOCK_CAP_MODULE_NAME));
    }

    @Test
    public void setCapModuleName_nothing_setsNewValue() throws Exception {
        // arrange
        assertThat(mReportData.getCapModuleName(), is(MOCK_CAP_MODULE_NAME));

        // act
        final String newValue = "newValue";
        mReportData.setCapModuleName(newValue);

        // assert
        assertThat(mReportData.getCapModuleName(), is(newValue));
    }

    @Test
    public void getCapModuleVersion_nothing_returnsCapModuleVersion() throws Exception {
        // act
        final String result = mReportData.getCapModuleVersion();

        // assert
        assertThat(result, is(MOCK_CAP_MODULE_VERSION));
    }

    @Test
    public void setCapModuleVersion_nothing_setsNewValue() throws Exception {
        // arrange
        assertThat(mReportData.getCapModuleVersion(), is(MOCK_CAP_MODULE_VERSION));

        // act
        final String newValue = "newValue";
        mReportData.setCapModuleVersion(newValue);

        // assert
        assertThat(mReportData.getCapModuleVersion(), is(newValue));
    }

    @Test
    public void getCapProcessName_nothing_returnsCapProcessName() throws Exception {
        // act
        final String result = mReportData.getCapProcessName();

        // assert
        assertThat(result, is(MOCK_CAP_PROCESS_NAME));
    }

    @Test
    public void setCapProcessName_nothing_setsNewValue() throws Exception {
        // arrange
        assertThat(mReportData.getCapProcessName(), is(MOCK_CAP_PROCESS_NAME));

        // act
        final String newValue = "newValue";
        mReportData.setCapProcessName(newValue);

        // assert
        assertThat(mReportData.getCapProcessName(), is(newValue));
    }

    @Test
    public void getCapProcessVersion_nothing_returnsCapProcessVersion() throws Exception {
        // act
        final String result = mReportData.getCapProcessVersion();

        // assert
        assertThat(result, is(MOCK_CAP_PROCESS_VERSION));
    }

    @Test
    public void setCapProcessVersion_nothing_setsNewValue() throws Exception {
        // arrange
        assertThat(mReportData.getCapProcessVersion(), is(MOCK_CAP_PROCESS_VERSION));

        // act
        final String newValue = "newValue";
        mReportData.setCapProcessVersion(newValue);

        // assert
        assertThat(mReportData.getCapProcessVersion(), is(newValue));
    }

    @Test
    public void getCapGUID_nothing_returnsCapGUID() throws Exception {
        // act
        final String result = mReportData.getCapGUID();

        // assert
        assertThat(result, is(MOCK_CAP_GUID));
    }

    @Test
    public void setCapGUID_nothing_setsNewValue() throws Exception {
        // arrange
        assertThat(mReportData.getCapGUID(), is(MOCK_CAP_GUID));

        // act
        final String newValue = "newValue";
        mReportData.setCapGUID(newValue);

        // assert
        assertThat(mReportData.getCapGUID(), is(newValue));
    }

    @Test
    public void getPublishAttempts_nothing_returnsPublishAttempts() throws Exception {
        // arrange
        assertThat(mReportData.getPublishAttempts(), is(MOCK_PUBLISH_ATTEMPTS));

        // act
        final String newValue = "newValue";
        mReportData.setCapGUID(newValue);

        // assert
        assertThat(mReportData.getCapGUID(), is(newValue));
    }

    @Test
    public void setPublishAttempts_nothing_setsNewValue() throws Exception {
        // act
        final int result = mReportData.getPublishAttempts();

        // act
        assertThat(result, is(MOCK_PUBLISH_ATTEMPTS));
    }

    @Test
    public void incrementPublishAttempts_nothing_incrementsValue() throws Exception {
        // arrange
        assertThat(mReportData.getPublishAttempts(), is(MOCK_PUBLISH_ATTEMPTS));

        // act
        mReportData.incrementsPublishAttempts();

        // assert
        assertThat(mReportData.getPublishAttempts(), is(MOCK_PUBLISH_ATTEMPTS + 1));
    }

    @Test
    public void getLastAttemptTimestamp_nothing_returnsPublishAttempts() throws Exception {
        // act
        final long result = mReportData.getLastAttemptTimestamp();

        // act
        assertThat(result, is(MOCK_LAST_ATTEMPT_TIMESTAMP));
    }

    @Test
    public void setLastAttemptTimestamp_nothing_setsNewValue() throws Exception {
        // arrange
        assertThat(mReportData.getPublishAttempts(), is(MOCK_PUBLISH_ATTEMPTS));

        // act
        final int newValue = 1234;
        mReportData.setPublishAttempts(newValue);

        // assert
        assertThat(mReportData.getPublishAttempts(), is(newValue));
    }

}
