package co.smartreceipts.android.imports;
import android.content.ContentResolver;
import android.content.Context;
import android.graphics.Bitmap;
import android.net.Uri;
import android.support.annotation.NonNull;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.Shadows;
import org.robolectric.shadows.ShadowBitmap;
import org.robolectric.shadows.ShadowMatrix;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import co.smartreceipts.android.TestResourceReader;
import co.smartreceipts.android.model.Trip;
import co.smartreceipts.android.settings.UserPreferenceManager;
import co.smartreceipts.android.settings.catalog.UserPreference;
import io.reactivex.Single;
import io.reactivex.observers.TestObserver;
import wb.android.storage.StorageManager;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@RunWith(RobolectricTestRunner.class)
public class ImageImportProcessorTest {
/**
* A simple test image that is 550x400
*/
private static final String SAMPLE_JPG = "sample.jpg";
/**
* A simple test image that is 2200x1600, which we scale down for storage reasons
*/
private static final String SAMPLE_JPG_BIG = "sample_big.jpg";
/**
* A simple test image that is 400x550, but it contains exif information to rotate back upright
*/
private static final String SAMPLE_JPG_WITH_EXIF = "sample_with_exif_to_rotate.jpg";
// Class under test
ImageImportProcessor mImportProcessor;
Context mContext;
TestObserver<File> mTestObserver;
File mDestination;
@Mock
Trip mTrip;
@Mock
StorageManager mStorageManner;
@Mock
UserPreferenceManager mPreferences;
@Mock
ContentResolver mContentResolver;
@Captor
ArgumentCaptor<Bitmap> mBitmapCaptor;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mTestObserver = new TestObserver<>();
mDestination = new File(mContext.getCacheDir(), "test.jpg");
when(mTrip.getDirectory()).thenReturn(mContext.getCacheDir());
when(mStorageManner.getFile(any(File.class), anyString())).thenReturn(mDestination);
when(mPreferences.get(UserPreference.Camera.SaveImagesInGrayScale)).thenReturn(false);
mImportProcessor = new ImageImportProcessor(mTrip, mStorageManner, mPreferences, mContext, mContentResolver);
}
@Test
public void importUriWithNullStream() throws Exception {
final Uri uri = mock(Uri.class);
when(mContentResolver.openInputStream(uri)).thenReturn(null);
final Single<File> resultSingle = mImportProcessor.process(uri);
resultSingle.subscribe(mTestObserver);
mTestObserver.assertError(FileNotFoundException.class);
}
@Test
public void importUriThrowsFileNotFoundException() throws Exception {
final Uri uri = mock(Uri.class);
when(mContentResolver.openInputStream(uri)).thenThrow(new FileNotFoundException("test"));
final Single<File> resultObservable = mImportProcessor.process(uri);
resultObservable.subscribe(mTestObserver);
mTestObserver.assertError(FileNotFoundException.class);
}
@Test
public void importUriWhenSaveFails() throws Exception {
final Uri uri = Uri.fromFile(mDestination);
configureUriForStream(uri, SAMPLE_JPG);
when(mPreferences.get(UserPreference.Camera.AutomaticallyRotateImages)).thenReturn(false);
when(mStorageManner.writeBitmap(any(Uri.class), mBitmapCaptor.capture(), eq(Bitmap.CompressFormat.JPEG), eq(85))).thenReturn(false);
final Single<File> resultObservable = mImportProcessor.process(uri);
resultObservable.subscribe(mTestObserver);
mTestObserver.assertError(IOException.class);
}
@Test
public void importUriWithoutAlterations() throws Exception {
final Uri uri = Uri.fromFile(mDestination);
configureUriForStream(uri, SAMPLE_JPG);
when(mPreferences.get(UserPreference.Camera.AutomaticallyRotateImages)).thenReturn(false);
when(mStorageManner.writeBitmap(any(Uri.class), mBitmapCaptor.capture(), eq(Bitmap.CompressFormat.JPEG), eq(85))).thenReturn(true);
final Single<File> resultObservable = mImportProcessor.process(uri);
resultObservable.subscribe(mTestObserver);
mTestObserver.assertValue(mDestination);
mTestObserver.assertComplete();
final Bitmap bitmap = mBitmapCaptor.getValue();
assertNotNull(bitmap);
assertEquals(550, bitmap.getWidth());
assertEquals(400, bitmap.getHeight());
}
@Test
public void importExifUriWithoutAlterations() throws Exception {
final Uri uri = Uri.fromFile(mDestination);
configureUriForStream(uri, SAMPLE_JPG_WITH_EXIF);
when(mPreferences.get(UserPreference.Camera.AutomaticallyRotateImages)).thenReturn(false);
when(mStorageManner.writeBitmap(any(Uri.class), mBitmapCaptor.capture(), eq(Bitmap.CompressFormat.JPEG), eq(85))).thenReturn(true);
final Single<File> resultObservable = mImportProcessor.process(uri);
resultObservable.subscribe(mTestObserver);
mTestObserver.assertValue(mDestination);
mTestObserver.assertComplete();
final Bitmap bitmap = mBitmapCaptor.getValue();
assertNotNull(bitmap);
// Confirm that it's sideways
assertEquals(400, bitmap.getWidth());
assertEquals(550, bitmap.getHeight());
}
@Test
public void importUriScalesDownSizes() throws Exception {
final Uri uri = Uri.fromFile(mDestination);
configureUriForStream(uri, SAMPLE_JPG_BIG);
when(mPreferences.get(UserPreference.Camera.AutomaticallyRotateImages)).thenReturn(false);
when(mStorageManner.writeBitmap(any(Uri.class), mBitmapCaptor.capture(), eq(Bitmap.CompressFormat.JPEG), eq(85))).thenReturn(true);
final Single<File> resultObservable = mImportProcessor.process(uri);
resultObservable.subscribe(mTestObserver);
mTestObserver.assertValue(mDestination);
mTestObserver.assertComplete();
// Note: we only scale down til one dimension is < 1024
final Bitmap bitmap = mBitmapCaptor.getValue();
assertNotNull(bitmap);
assertEquals(1100, bitmap.getWidth());
assertEquals(800, bitmap.getHeight());
}
@Test
public void importUriWithRotateOn() throws Exception {
final Uri uri = Uri.fromFile(mDestination);
configureUriForStream(uri, SAMPLE_JPG);
when(mPreferences.get(UserPreference.Camera.AutomaticallyRotateImages)).thenReturn(true);
when(mStorageManner.writeBitmap(any(Uri.class), mBitmapCaptor.capture(), eq(Bitmap.CompressFormat.JPEG), eq(85))).thenReturn(true);
final Single<File> resultObservable = mImportProcessor.process(uri);
resultObservable.subscribe(mTestObserver);
mTestObserver.assertValue(mDestination);
mTestObserver.assertComplete();
final Bitmap bitmap = mBitmapCaptor.getValue();
assertNotNull(bitmap);
assertEquals(550, bitmap.getWidth());
assertEquals(400, bitmap.getHeight());
}
@Test
public void importExifUriWithRotateOn() throws Exception {
final Uri uri = Uri.fromFile(mDestination);
configureUriForStream(uri, SAMPLE_JPG_WITH_EXIF);
when(mPreferences.get(UserPreference.Camera.AutomaticallyRotateImages)).thenReturn(true);
when(mStorageManner.writeBitmap(any(Uri.class), mBitmapCaptor.capture(), eq(Bitmap.CompressFormat.JPEG), eq(85))).thenReturn(true);
final Single<File> resultObservable = mImportProcessor.process(uri);
resultObservable.subscribe(mTestObserver);
mTestObserver.assertValue(mDestination);
mTestObserver.assertComplete();
final Bitmap bitmap = mBitmapCaptor.getValue();
assertNotNull(bitmap);
// TODO: Use direct getWidth/getHeight test once Robolectric 3.2 is available
// TODO: Remove this shadow hack once the Robolectric supports this rotation via ShadowBitmap
// TODO: assertEquals(550, bitmap.getWidth());
// TODO: assertEquals(400, bitmap.getHeight());
// Confirm that we have a matrix to rotate this
final ShadowBitmap shadowBitmap = Shadows.shadowOf(bitmap);
final ShadowMatrix shadowMatrix = Shadows.shadowOf(shadowBitmap.getCreatedFromMatrix());
assertEquals(shadowMatrix.getSetOperations().get(ShadowMatrix.ROTATE), "90.0");
}
private void configureUriForStream(@NonNull Uri uri, @NonNull String imageFile) throws Exception {
when(mContentResolver.openInputStream(uri)).thenReturn(new TestResourceReader().openStream(imageFile), new TestResourceReader().openStream(imageFile), new TestResourceReader().openStream(imageFile));
}
}