package com.getkeepsafe.relinker;

import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.os.Build;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;

import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;


@RunWith(RobolectricTestRunner.class)
@Config(sdk = Build.VERSION_CODES.LOLLIPOP)
public class ApkLibraryInstallerWithSplitsTest {

    @Rule
    public TemporaryFolder tempFolder = new TemporaryFolder();

    @Mock Context context;

    @Mock ApplicationInfo applicationInfo;

    @Mock ReLinkerInstance instance;

    private final String[] abis = new String[]{"x86"};

    private ApkLibraryInstaller subject;

    @Before
    public void setUp() throws IOException {
        MockitoAnnotations.initMocks(this);
        tempFolder.create();
        subject = new ApkLibraryInstaller();
    }

    @Test
    public void nullSplitSourceDirInstallCorrectly() throws IOException {
        final File destination = tempFolder.newFile("null-test");

        applicationInfo.sourceDir = getClass().getResource("/fake.apk").getFile();
        applicationInfo.splitSourceDirs = null;
        when(context.getApplicationInfo()).thenReturn(applicationInfo);

        subject.installLibrary(context, abis, "libtest.so", destination, instance);
        verify(context).getApplicationInfo();
        assertThat(fileToString(destination), is("works!"));
    }

    @Test
    public void emptySplitSourceDirInstallCorrectly() throws IOException {
        final File destination = tempFolder.newFile("empty-test");

        applicationInfo.sourceDir = getClass().getResource("/fake.apk").getFile();
        applicationInfo.splitSourceDirs = new String[]{};
        when(context.getApplicationInfo()).thenReturn(applicationInfo);

        subject.installLibrary(context, abis, "libtest.so", destination, instance);
        verify(context).getApplicationInfo();
        assertThat(fileToString(destination), is("works!"));
    }

    @Test
    public void apkSplitsInstallCorrectly() throws IOException {
        final File destination = tempFolder.newFile("split-test");

        applicationInfo.sourceDir = "/fake/path/nolib.apk";
        String actualApk = getClass().getResource("/fake.apk").getFile();
        applicationInfo.splitSourceDirs = new String[]{"/another/fake/path/nolib.apk", actualApk};
        when(context.getApplicationInfo()).thenReturn(applicationInfo);

        subject.installLibrary(context, abis, "libtest.so", destination, instance);
        verify(context).getApplicationInfo();
        assertThat(fileToString(destination), is("works!"));
    }

    private String fileToString(final File file) throws IOException {
        final long size = file.length();
        if (size > Integer.MAX_VALUE) {
            throw new IOException("Can't read a file larger than Integer.MAX_VALUE");
        }

        final byte[] data = new byte[(int) size];
        final FileInputStream in = new FileInputStream(file);
        try {
            int bytesRead = 0;
            while (bytesRead < size) {
                int read = in.read(data, bytesRead, (int) size - bytesRead);
                if (read == -1) {
                    throw new EOFException();
                }
                bytesRead += read;
            }

            return new String(data);
        } finally {
            in.close();
        }
    }
}