[go: nahoru, domu]

blob: fdb7220f87c88bc8ff9e383307a29a88be241307 [file] [log] [blame]
Trevor McGuirec63cd732019-02-06 13:12:50 -08001/*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package androidx.camera.core;
18
19import static com.google.common.truth.Truth.assertThat;
Trevor McGuire470a44f2019-02-11 13:19:37 -080020
Jelle Fresenfbb001b2020-01-16 15:46:29 +000021import static org.mockito.ArgumentMatchers.any;
22import static org.mockito.ArgumentMatchers.anyString;
23import static org.mockito.ArgumentMatchers.eq;
Trevor McGuirec63cd732019-02-06 13:12:50 -080024import static org.mockito.Mockito.mock;
25import static org.mockito.Mockito.verify;
26import static org.mockito.Mockito.when;
27
Xi Zhangec5384d2020-01-27 15:01:28 -080028import android.Manifest;
29import android.content.ContentResolver;
30import android.content.ContentValues;
Trevor McGuirec63cd732019-02-06 13:12:50 -080031import android.graphics.Bitmap;
32import android.graphics.BitmapFactory;
33import android.graphics.ImageFormat;
TY Chang8d879362019-02-21 15:22:15 +080034import android.graphics.Rect;
Xi Zhangec5384d2020-01-27 15:01:28 -080035import android.net.Uri;
Xi Zhang6f28a572020-02-10 10:50:01 -080036import android.os.Environment;
Xi Zhangec5384d2020-01-27 15:01:28 -080037import android.os.ParcelFileDescriptor;
38import android.provider.MediaStore;
Trevor McGuirec63cd732019-02-06 13:12:50 -080039import android.util.Base64;
Xi Zhang6f28a572020-02-10 10:50:01 -080040import android.util.Log;
Trevor McGuire470a44f2019-02-11 13:19:37 -080041
Xi Zhangec5384d2020-01-27 15:01:28 -080042import androidx.annotation.NonNull;
Trevor McGuire470a44f2019-02-11 13:19:37 -080043import androidx.annotation.Nullable;
Franklin Wu3b4ed3e2019-09-13 13:43:29 -070044import androidx.camera.core.ImageSaver.OnImageSavedCallback;
Trevor McGuirec63cd732019-02-06 13:12:50 -080045import androidx.camera.core.ImageSaver.SaveError;
Xi Zhangec5384d2020-01-27 15:01:28 -080046import androidx.test.core.app.ApplicationProvider;
Trevor McGuireaa17f672019-02-14 15:14:46 -080047import androidx.test.ext.junit.runners.AndroidJUnit4;
Sam Gilberte2124052019-05-03 18:36:32 -040048import androidx.test.filters.MediumTest;
Xi Zhangec5384d2020-01-27 15:01:28 -080049import androidx.test.rule.GrantPermissionRule;
Trevor McGuire470a44f2019-02-11 13:19:37 -080050
Trevor McGuirec63cd732019-02-06 13:12:50 -080051import org.junit.After;
52import org.junit.Before;
Xi Zhangec5384d2020-01-27 15:01:28 -080053import org.junit.Rule;
Trevor McGuirec63cd732019-02-06 13:12:50 -080054import org.junit.Test;
55import org.junit.runner.RunWith;
Xi Zhangec5384d2020-01-27 15:01:28 -080056import org.mockito.ArgumentCaptor;
Trevor McGuirec63cd732019-02-06 13:12:50 -080057import org.mockito.Mock;
58
Trevor McGuire470a44f2019-02-11 13:19:37 -080059import java.io.File;
Xi Zhangec5384d2020-01-27 15:01:28 -080060import java.io.FileNotFoundException;
61import java.io.FileOutputStream;
Trevor McGuire470a44f2019-02-11 13:19:37 -080062import java.io.IOException;
Xi Zhangec5384d2020-01-27 15:01:28 -080063import java.io.OutputStream;
Trevor McGuire470a44f2019-02-11 13:19:37 -080064import java.nio.ByteBuffer;
WenHung_Teng5cc433b2019-08-26 11:40:45 +080065import java.util.concurrent.ExecutorService;
66import java.util.concurrent.Executors;
Trevor McGuire470a44f2019-02-11 13:19:37 -080067import java.util.concurrent.Semaphore;
68
Xi Zhangaa5b7ad2020-03-30 12:27:48 -070069/**
70 * Instrument tests for {@link ImageSaver}.
71 */
Sam Gilberte2124052019-05-03 18:36:32 -040072@MediumTest
Trevor McGuirec63cd732019-02-06 13:12:50 -080073@RunWith(AndroidJUnit4.class)
Trevor McGuiree85de572019-02-15 20:20:24 -080074public class ImageSaverTest {
Trevor McGuirec63cd732019-02-06 13:12:50 -080075
Trevor McGuire470a44f2019-02-11 13:19:37 -080076 private static final int WIDTH = 160;
77 private static final int HEIGHT = 120;
TY Chang8d879362019-02-21 15:22:15 +080078 private static final int CROP_WIDTH = 100;
79 private static final int CROP_HEIGHT = 100;
Trevor McGuire470a44f2019-02-11 13:19:37 -080080 private static final int Y_PIXEL_STRIDE = 1;
81 private static final int Y_ROW_STRIDE = WIDTH;
82 private static final int UV_PIXEL_STRIDE = 1;
83 private static final int UV_ROW_STRIDE = WIDTH / 2;
Trevor McGuire3ae3a1d2019-02-14 13:48:45 -080084 private static final String JPEG_IMAGE_DATA_BASE_64 =
Trevor McGuire470a44f2019-02-11 13:19:37 -080085 "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB"
86 + "AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEB"
87 + "AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wAARCAB4AKADASIA"
88 + "AhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQA"
89 + "AAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3"
90 + "ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWm"
91 + "p6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEA"
92 + "AwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSEx"
93 + "BhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElK"
94 + "U1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3"
95 + "uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD/AD/6"
96 + "KKK/8/8AP/P/AAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiii"
97 + "gAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKA"
98 + "CiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAK"
99 + "KKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAoo"
100 + "ooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiii"
101 + "gAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA//9k=";
Trevor McGuire6bf813c2019-02-26 10:26:12 -0800102 // The image used here has a YUV_420_888 format.
Xi Zhangec5384d2020-01-27 15:01:28 -0800103
Xi Zhang6f28a572020-02-10 10:50:01 -0800104 private static final String TAG = "ImageSaverTest";
Xi Zhangaa5b7ad2020-03-30 12:27:48 -0700105 private static final String INVALID_DATA_PATH = "/invalid_path";
Xi Zhang6f28a572020-02-10 10:50:01 -0800106
Xi Zhangec5384d2020-01-27 15:01:28 -0800107 @Rule
108 public GrantPermissionRule mStoragePermissionRule =
109 GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE,
110 Manifest.permission.READ_EXTERNAL_STORAGE);
111
Trevor McGuire6bf813c2019-02-26 10:26:12 -0800112 @Mock
113 private final ImageProxy mMockYuvImage = mock(ImageProxy.class);
114 @Mock
115 private final ImageProxy.PlaneProxy mYPlane = mock(ImageProxy.PlaneProxy.class);
116 @Mock
117 private final ImageProxy.PlaneProxy mUPlane = mock(ImageProxy.PlaneProxy.class);
118 @Mock
119 private final ImageProxy.PlaneProxy mVPlane = mock(ImageProxy.PlaneProxy.class);
120 private final ByteBuffer mYBuffer = ByteBuffer.allocateDirect(WIDTH * HEIGHT);
121 private final ByteBuffer mUBuffer = ByteBuffer.allocateDirect(WIDTH * HEIGHT / 4);
122 private final ByteBuffer mVBuffer = ByteBuffer.allocateDirect(WIDTH * HEIGHT / 4);
123 @Mock
124 private final ImageProxy mMockJpegImage = mock(ImageProxy.class);
125 @Mock
126 private final ImageProxy.PlaneProxy mJpegDataPlane = mock(ImageProxy.PlaneProxy.class);
Trevor McGuire3ae3a1d2019-02-14 13:48:45 -0800127 private final ByteBuffer mJpegDataBuffer =
128 ByteBuffer.wrap(Base64.decode(JPEG_IMAGE_DATA_BASE_64, Base64.DEFAULT));
Trevor McGuirec63cd732019-02-06 13:12:50 -0800129
Trevor McGuire3ae3a1d2019-02-14 13:48:45 -0800130 private final Semaphore mSemaphore = new Semaphore(0);
Franklin Wu3b4ed3e2019-09-13 13:43:29 -0700131 private final ImageSaver.OnImageSavedCallback mMockCallback =
132 mock(ImageSaver.OnImageSavedCallback.class);
133 private final ImageSaver.OnImageSavedCallback mSyncCallback =
134 new OnImageSavedCallback() {
Trevor McGuire470a44f2019-02-11 13:19:37 -0800135 @Override
Xi Zhangec5384d2020-01-27 15:01:28 -0800136 public void onImageSaved(
137 @NonNull ImageCapture.OutputFileResults outputFileResults) {
138 mMockCallback.onImageSaved(outputFileResults);
Trevor McGuire3ae3a1d2019-02-14 13:48:45 -0800139 mSemaphore.release();
Trevor McGuire470a44f2019-02-11 13:19:37 -0800140 }
Trevor McGuirec63cd732019-02-06 13:12:50 -0800141
Trevor McGuire470a44f2019-02-11 13:19:37 -0800142 @Override
husaynhakeem661b7e92019-11-22 16:39:36 -0800143 public void onError(SaveError saveError, String message,
husaynhakeemd31b4c52019-11-11 10:23:35 -0800144 @Nullable Throwable cause) {
Xi Zhang6f28a572020-02-10 10:50:01 -0800145 Log.d(TAG, message, cause);
Franklin Wu3b4ed3e2019-09-13 13:43:29 -0700146 mMockCallback.onError(saveError, message, cause);
Trevor McGuire3ae3a1d2019-02-14 13:48:45 -0800147 mSemaphore.release();
Trevor McGuire470a44f2019-02-11 13:19:37 -0800148 }
149 };
Trevor McGuirec63cd732019-02-06 13:12:50 -0800150
WenHung_Teng5cc433b2019-08-26 11:40:45 +0800151 private ExecutorService mBackgroundExecutor;
Xi Zhangec5384d2020-01-27 15:01:28 -0800152 private ContentResolver mContentResolver;
Trevor McGuirec63cd732019-02-06 13:12:50 -0800153
Trevor McGuire470a44f2019-02-11 13:19:37 -0800154 @Before
155 public void setup() {
Xi Zhang6f28a572020-02-10 10:50:01 -0800156 createDefaultPictureFolderIfNotExist();
Trevor McGuire470a44f2019-02-11 13:19:37 -0800157 // The YUV image's behavior.
Trevor McGuire3ae3a1d2019-02-14 13:48:45 -0800158 when(mMockYuvImage.getFormat()).thenReturn(ImageFormat.YUV_420_888);
159 when(mMockYuvImage.getWidth()).thenReturn(WIDTH);
160 when(mMockYuvImage.getHeight()).thenReturn(HEIGHT);
Trevor McGuirec63cd732019-02-06 13:12:50 -0800161
Trevor McGuire3ae3a1d2019-02-14 13:48:45 -0800162 when(mYPlane.getBuffer()).thenReturn(mYBuffer);
163 when(mYPlane.getPixelStride()).thenReturn(Y_PIXEL_STRIDE);
164 when(mYPlane.getRowStride()).thenReturn(Y_ROW_STRIDE);
Trevor McGuirec63cd732019-02-06 13:12:50 -0800165
Trevor McGuire3ae3a1d2019-02-14 13:48:45 -0800166 when(mUPlane.getBuffer()).thenReturn(mUBuffer);
167 when(mUPlane.getPixelStride()).thenReturn(UV_PIXEL_STRIDE);
168 when(mUPlane.getRowStride()).thenReturn(UV_ROW_STRIDE);
Trevor McGuirec63cd732019-02-06 13:12:50 -0800169
Trevor McGuire3ae3a1d2019-02-14 13:48:45 -0800170 when(mVPlane.getBuffer()).thenReturn(mVBuffer);
171 when(mVPlane.getPixelStride()).thenReturn(UV_PIXEL_STRIDE);
172 when(mVPlane.getRowStride()).thenReturn(UV_ROW_STRIDE);
173 when(mMockYuvImage.getPlanes())
174 .thenReturn(new ImageProxy.PlaneProxy[]{mYPlane, mUPlane, mVPlane});
TY Chang8d879362019-02-21 15:22:15 +0800175 when(mMockYuvImage.getCropRect()).thenReturn(new Rect(0, 0, CROP_WIDTH, CROP_HEIGHT));
Trevor McGuirec63cd732019-02-06 13:12:50 -0800176
Trevor McGuire470a44f2019-02-11 13:19:37 -0800177 // The JPEG image's behavior
Trevor McGuire3ae3a1d2019-02-14 13:48:45 -0800178 when(mMockJpegImage.getFormat()).thenReturn(ImageFormat.JPEG);
179 when(mMockJpegImage.getWidth()).thenReturn(WIDTH);
180 when(mMockJpegImage.getHeight()).thenReturn(HEIGHT);
TY Chang8d879362019-02-21 15:22:15 +0800181 when(mMockJpegImage.getCropRect()).thenReturn(new Rect(0, 0, CROP_WIDTH, CROP_HEIGHT));
Trevor McGuirec63cd732019-02-06 13:12:50 -0800182
Trevor McGuire3ae3a1d2019-02-14 13:48:45 -0800183 when(mJpegDataPlane.getBuffer()).thenReturn(mJpegDataBuffer);
184 when(mMockJpegImage.getPlanes()).thenReturn(new ImageProxy.PlaneProxy[]{mJpegDataPlane});
Trevor McGuirec63cd732019-02-06 13:12:50 -0800185
WenHung_Teng5cc433b2019-08-26 11:40:45 +0800186 // Set up a background executor for callbacks
187 mBackgroundExecutor = Executors.newSingleThreadExecutor();
Xi Zhangec5384d2020-01-27 15:01:28 -0800188
189 mContentResolver = ApplicationProvider.getApplicationContext().getContentResolver();
Trevor McGuire470a44f2019-02-11 13:19:37 -0800190 }
Trevor McGuirec63cd732019-02-06 13:12:50 -0800191
Trevor McGuire470a44f2019-02-11 13:19:37 -0800192 @After
193 public void tearDown() {
WenHung_Teng5cc433b2019-08-26 11:40:45 +0800194 mBackgroundExecutor.shutdown();
Trevor McGuire470a44f2019-02-11 13:19:37 -0800195 }
Trevor McGuirec63cd732019-02-06 13:12:50 -0800196
Xi Zhang6f28a572020-02-10 10:50:01 -0800197 private void createDefaultPictureFolderIfNotExist() {
198 File pictureFolder = Environment.getExternalStoragePublicDirectory(
199 Environment.DIRECTORY_PICTURES);
200 if (!pictureFolder.exists()) {
201 pictureFolder.mkdir();
202 }
203 }
204
Trevor McGuire470a44f2019-02-11 13:19:37 -0800205 private ImageSaver getDefaultImageSaver(ImageProxy image, File file) {
Xi Zhangec5384d2020-01-27 15:01:28 -0800206 return getDefaultImageSaver(image,
207 new ImageCapture.OutputFileOptions.Builder(file).build());
208 }
209
Xi Zhang6f28a572020-02-10 10:50:01 -0800210 private ImageSaver getDefaultImageSaver(@NonNull ImageProxy image) {
211 ContentValues contentValues = new ContentValues();
leo huanga78ac012020-05-18 19:28:44 +0800212 contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");
Xi Zhangec5384d2020-01-27 15:01:28 -0800213 return getDefaultImageSaver(image,
214 new ImageCapture.OutputFileOptions.Builder(mContentResolver,
215 MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
Xi Zhang6f28a572020-02-10 10:50:01 -0800216 contentValues).build());
Xi Zhangec5384d2020-01-27 15:01:28 -0800217 }
218
219 private ImageSaver getDefaultImageSaver(ImageProxy image, OutputStream outputStream) {
220 return getDefaultImageSaver(image,
221 new ImageCapture.OutputFileOptions.Builder(outputStream).build());
222 }
223
224 private ImageSaver getDefaultImageSaver(ImageProxy image,
225 ImageCapture.OutputFileOptions outputFileOptions) {
Trevor McGuire470a44f2019-02-11 13:19:37 -0800226 return new ImageSaver(
227 image,
Xi Zhangec5384d2020-01-27 15:01:28 -0800228 outputFileOptions,
Trevor McGuire470a44f2019-02-11 13:19:37 -0800229 /*orientation=*/ 0,
WenHung_Teng5cc433b2019-08-26 11:40:45 +0800230 mBackgroundExecutor,
Franklin Wu3b4ed3e2019-09-13 13:43:29 -0700231 mSyncCallback);
Trevor McGuire470a44f2019-02-11 13:19:37 -0800232 }
Trevor McGuirec63cd732019-02-06 13:12:50 -0800233
Trevor McGuire470a44f2019-02-11 13:19:37 -0800234 @Test
235 public void canSaveYuvImage() throws InterruptedException, IOException {
236 File saveLocation = File.createTempFile("test", ".jpg");
237 saveLocation.deleteOnExit();
Trevor McGuirec63cd732019-02-06 13:12:50 -0800238
Xi Zhangec5384d2020-01-27 15:01:28 -0800239 getDefaultImageSaver(mMockYuvImage, saveLocation).run();
Trevor McGuire3ae3a1d2019-02-14 13:48:45 -0800240 mSemaphore.acquire();
Trevor McGuirec63cd732019-02-06 13:12:50 -0800241
Xi Zhangec5384d2020-01-27 15:01:28 -0800242 verify(mMockCallback).onImageSaved(any());
243 }
244
245 @Test
246 public void saveToUri() throws InterruptedException, FileNotFoundException {
247 // Act.
248 getDefaultImageSaver(mMockYuvImage).run();
249 mSemaphore.acquire();
250
251 // Assert.
252 // Verify success callback is called.
253 ArgumentCaptor<ImageCapture.OutputFileResults> outputFileResultsArgumentCaptor =
254 ArgumentCaptor.forClass(ImageCapture.OutputFileResults.class);
255 verify(mMockCallback).onImageSaved(outputFileResultsArgumentCaptor.capture());
256
257 // Verify save location Uri is available.
258 Uri saveLocationUri = outputFileResultsArgumentCaptor.getValue().getSavedUri();
259 assertThat(saveLocationUri).isNotNull();
260
261 // Loads image and verify width and height.
262 ParcelFileDescriptor pfd = mContentResolver.openFileDescriptor(saveLocationUri, "r");
263 Bitmap bitmap = BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor());
264 assertThat(bitmap.getWidth()).isEqualTo(CROP_WIDTH);
265 assertThat(bitmap.getHeight()).isEqualTo(CROP_HEIGHT);
leo huang6c2ef902020-03-12 19:43:27 +0800266
267 // Clean up.
268 mContentResolver.delete(saveLocationUri, null, null);
Xi Zhangec5384d2020-01-27 15:01:28 -0800269 }
270
271 @Test
Xi Zhangef6f7032020-03-31 09:06:29 -0700272 public void saveToUriWithEmptyCollection_onErrorCalled() throws InterruptedException {
Xi Zhang6f28a572020-02-10 10:50:01 -0800273 // Arrange.
274 ContentValues contentValues = new ContentValues();
leo huanga78ac012020-05-18 19:28:44 +0800275 contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");
Xi Zhang6f28a572020-02-10 10:50:01 -0800276 contentValues.put(MediaStore.MediaColumns.DATA, INVALID_DATA_PATH);
277 ImageSaver imageSaver = getDefaultImageSaver(mMockYuvImage,
278 new ImageCapture.OutputFileOptions.Builder(mContentResolver,
Xi Zhangef6f7032020-03-31 09:06:29 -0700279 Uri.EMPTY,
Xi Zhang6f28a572020-02-10 10:50:01 -0800280 contentValues).build());
281
282 // Act.
283 imageSaver.run();
284 mSemaphore.acquire();
285
286 // Assert.
Xi Zhang127e6ba2020-02-13 14:36:25 -0800287 verify(mMockCallback).onError(eq(SaveError.FILE_IO_FAILED), any(), any());
Xi Zhang6f28a572020-02-10 10:50:01 -0800288 }
289
290 @Test
Xi Zhangec5384d2020-01-27 15:01:28 -0800291 public void saveToOutputStream() throws InterruptedException, IOException {
292 // Arrange.
293 File file = File.createTempFile("test", ".jpg");
294 file.deleteOnExit();
295
296 // Act.
297 try (OutputStream outputStream = new FileOutputStream(file)) {
298 getDefaultImageSaver(mMockYuvImage, outputStream).run();
299 mSemaphore.acquire();
300 }
301
302 // Assert.
303 verify(mMockCallback).onImageSaved(any());
304 // Loads image and verify width and height.
305 Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
306 assertThat(bitmap.getWidth()).isEqualTo(CROP_WIDTH);
307 assertThat(bitmap.getHeight()).isEqualTo(CROP_HEIGHT);
308 }
309
310 @Test
311 public void saveToClosedOutputStream_onErrorCalled() throws InterruptedException,
312 IOException {
313 // Arrange.
314 File file = File.createTempFile("test", ".jpg");
315 file.deleteOnExit();
316 OutputStream outputStream = new FileOutputStream(file);
317 outputStream.close();
318
319 // Act.
320 getDefaultImageSaver(mMockYuvImage, outputStream).run();
321 mSemaphore.acquire();
322
323 // Assert.
324 verify(mMockCallback).onError(eq(SaveError.FILE_IO_FAILED), anyString(),
325 any(Throwable.class));
Trevor McGuire470a44f2019-02-11 13:19:37 -0800326 }
Trevor McGuirec63cd732019-02-06 13:12:50 -0800327
Trevor McGuire470a44f2019-02-11 13:19:37 -0800328 @Test
329 public void canSaveJpegImage() throws InterruptedException, IOException {
330 File saveLocation = File.createTempFile("test", ".jpg");
331 saveLocation.deleteOnExit();
Trevor McGuirec63cd732019-02-06 13:12:50 -0800332
Xi Zhangec5384d2020-01-27 15:01:28 -0800333 getDefaultImageSaver(mMockJpegImage, saveLocation).run();
Trevor McGuire3ae3a1d2019-02-14 13:48:45 -0800334 mSemaphore.acquire();
Trevor McGuirec63cd732019-02-06 13:12:50 -0800335
Xi Zhangec5384d2020-01-27 15:01:28 -0800336 verify(mMockCallback).onImageSaved(any());
Trevor McGuire470a44f2019-02-11 13:19:37 -0800337 }
Trevor McGuirec63cd732019-02-06 13:12:50 -0800338
Trevor McGuire470a44f2019-02-11 13:19:37 -0800339 @Test
Trevor McGuire3ae3a1d2019-02-14 13:48:45 -0800340 public void errorCallbackWillBeCalledOnInvalidPath() throws InterruptedException {
Trevor McGuire470a44f2019-02-11 13:19:37 -0800341 // Invalid filename should cause error
342 File saveLocation = new File("/not/a/real/path.jpg");
Trevor McGuirec63cd732019-02-06 13:12:50 -0800343
Xi Zhangec5384d2020-01-27 15:01:28 -0800344 getDefaultImageSaver(mMockJpegImage, saveLocation).run();
Trevor McGuire3ae3a1d2019-02-14 13:48:45 -0800345 mSemaphore.acquire();
Trevor McGuirec63cd732019-02-06 13:12:50 -0800346
Franklin Wu3b4ed3e2019-09-13 13:43:29 -0700347 verify(mMockCallback).onError(eq(SaveError.FILE_IO_FAILED), anyString(),
Trevor McGuire6bf813c2019-02-26 10:26:12 -0800348 any(Throwable.class));
Trevor McGuire470a44f2019-02-11 13:19:37 -0800349 }
Trevor McGuirec63cd732019-02-06 13:12:50 -0800350
Trevor McGuire470a44f2019-02-11 13:19:37 -0800351 @Test
352 public void imageIsClosedOnSuccess() throws InterruptedException, IOException {
353 File saveLocation = File.createTempFile("test", ".jpg");
354 saveLocation.deleteOnExit();
Trevor McGuirec63cd732019-02-06 13:12:50 -0800355
Xi Zhangec5384d2020-01-27 15:01:28 -0800356 getDefaultImageSaver(mMockJpegImage, saveLocation).run();
Trevor McGuirec63cd732019-02-06 13:12:50 -0800357
Trevor McGuire3ae3a1d2019-02-14 13:48:45 -0800358 mSemaphore.acquire();
Trevor McGuirec63cd732019-02-06 13:12:50 -0800359
Trevor McGuire3ae3a1d2019-02-14 13:48:45 -0800360 verify(mMockJpegImage).close();
Trevor McGuire470a44f2019-02-11 13:19:37 -0800361 }
Trevor McGuirec63cd732019-02-06 13:12:50 -0800362
Trevor McGuire470a44f2019-02-11 13:19:37 -0800363 @Test
Xi Zhangec5384d2020-01-27 15:01:28 -0800364 public void imageIsClosedOnError() throws InterruptedException {
Trevor McGuire470a44f2019-02-11 13:19:37 -0800365 // Invalid filename should cause error
366 File saveLocation = new File("/not/a/real/path.jpg");
Trevor McGuirec63cd732019-02-06 13:12:50 -0800367
Xi Zhangec5384d2020-01-27 15:01:28 -0800368 getDefaultImageSaver(mMockJpegImage, saveLocation).run();
Trevor McGuire3ae3a1d2019-02-14 13:48:45 -0800369 mSemaphore.acquire();
Trevor McGuirec63cd732019-02-06 13:12:50 -0800370
Trevor McGuire3ae3a1d2019-02-14 13:48:45 -0800371 verify(mMockJpegImage).close();
Trevor McGuire470a44f2019-02-11 13:19:37 -0800372 }
Trevor McGuirec63cd732019-02-06 13:12:50 -0800373
Trevor McGuire470a44f2019-02-11 13:19:37 -0800374 private void imageCanBeCropped(ImageProxy image) throws InterruptedException, IOException {
375 File saveLocation = File.createTempFile("test", ".jpg");
376 saveLocation.deleteOnExit();
Trevor McGuirec63cd732019-02-06 13:12:50 -0800377
Xi Zhangec5384d2020-01-27 15:01:28 -0800378 getDefaultImageSaver(image, saveLocation).run();
Trevor McGuire3ae3a1d2019-02-14 13:48:45 -0800379 mSemaphore.acquire();
Trevor McGuirec63cd732019-02-06 13:12:50 -0800380
Trevor McGuire470a44f2019-02-11 13:19:37 -0800381 Bitmap bitmap = BitmapFactory.decodeFile(saveLocation.getPath());
Xi Zhangec5384d2020-01-27 15:01:28 -0800382 assertThat(bitmap.getWidth()).isEqualTo(CROP_WIDTH);
383 assertThat(bitmap.getHeight()).isEqualTo(CROP_HEIGHT);
Trevor McGuire470a44f2019-02-11 13:19:37 -0800384 }
Trevor McGuirec63cd732019-02-06 13:12:50 -0800385
Trevor McGuire470a44f2019-02-11 13:19:37 -0800386 @Test
387 public void jpegImageCanBeCropped() throws InterruptedException, IOException {
Trevor McGuire3ae3a1d2019-02-14 13:48:45 -0800388 imageCanBeCropped(mMockJpegImage);
Trevor McGuire470a44f2019-02-11 13:19:37 -0800389 }
Trevor McGuirec63cd732019-02-06 13:12:50 -0800390
Trevor McGuire470a44f2019-02-11 13:19:37 -0800391 @Test
392 public void yuvImageCanBeCropped() throws InterruptedException, IOException {
Trevor McGuire3ae3a1d2019-02-14 13:48:45 -0800393 imageCanBeCropped(mMockYuvImage);
Trevor McGuire470a44f2019-02-11 13:19:37 -0800394 }
Trevor McGuirec63cd732019-02-06 13:12:50 -0800395}