blob: 507f2aefe1a16970f42e2fb6bb807be6c5ba6cd6 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package io.flutter.plugins.camera;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
import android.app.Activity;
import android.content.Context;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.*;
import android.hardware.camera2.params.SessionConfiguration;
import android.media.CamcorderProfile;
import android.media.ImageReader;
import android.media.MediaRecorder;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Range;
import android.util.Size;
import android.view.Surface;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.LifecycleObserver;
import io.flutter.embedding.engine.systemchannels.PlatformChannel;
import io.flutter.plugin.common.EventChannel;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugins.camera.features.CameraFeatureFactory;
import io.flutter.plugins.camera.features.CameraFeatures;
import io.flutter.plugins.camera.features.Point;
import io.flutter.plugins.camera.features.autofocus.AutoFocusFeature;
import io.flutter.plugins.camera.features.autofocus.FocusMode;
import io.flutter.plugins.camera.features.exposurelock.ExposureLockFeature;
import io.flutter.plugins.camera.features.exposurelock.ExposureMode;
import io.flutter.plugins.camera.features.exposureoffset.ExposureOffsetFeature;
import io.flutter.plugins.camera.features.exposurepoint.ExposurePointFeature;
import io.flutter.plugins.camera.features.flash.FlashFeature;
import io.flutter.plugins.camera.features.flash.FlashMode;
import io.flutter.plugins.camera.features.focuspoint.FocusPointFeature;
import io.flutter.plugins.camera.features.fpsrange.FpsRangeFeature;
import io.flutter.plugins.camera.features.noisereduction.NoiseReductionFeature;
import io.flutter.plugins.camera.features.resolution.ResolutionFeature;
import io.flutter.plugins.camera.features.resolution.ResolutionPreset;
import io.flutter.plugins.camera.features.sensororientation.DeviceOrientationManager;
import io.flutter.plugins.camera.features.sensororientation.SensorOrientationFeature;
import io.flutter.plugins.camera.features.zoomlevel.ZoomLevelFeature;
import io.flutter.plugins.camera.media.ImageStreamReader;
import io.flutter.plugins.camera.utils.TestUtils;
import io.flutter.view.TextureRegistry;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.MockedConstruction;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
class FakeCameraDeviceWrapper implements CameraDeviceWrapper {
final List<CaptureRequest.Builder> captureRequests;
@Nullable final CameraCaptureSession session;
FakeCameraDeviceWrapper(List<CaptureRequest.Builder> captureRequests) {
this(captureRequests, null);
}
FakeCameraDeviceWrapper(
List<CaptureRequest.Builder> captureRequests, CameraCaptureSession session) {
this.captureRequests = captureRequests;
this.session = session;
}
@NonNull
@Override
public CaptureRequest.Builder createCaptureRequest(int var1) {
return captureRequests.remove(0);
}
@Override
public void createCaptureSession(SessionConfiguration config) {
if (session != null) {
config.getStateCallback().onConfigured(session);
}
}
@Override
public void createCaptureSession(
@NonNull List<Surface> outputs,
@NonNull CameraCaptureSession.StateCallback callback,
@Nullable Handler handler) {
if (session != null) {
callback.onConfigured(session);
}
}
@Override
public void close() {}
}
public class CameraTest {
private CameraProperties mockCameraProperties;
private CameraFeatureFactory mockCameraFeatureFactory;
private DartMessenger mockDartMessenger;
private Camera camera;
private CameraCaptureSession mockCaptureSession;
private CaptureRequest.Builder mockPreviewRequestBuilder;
private MockedStatic<Camera.HandlerThreadFactory> mockHandlerThreadFactory;
private HandlerThread mockHandlerThread;
private MockedStatic<Camera.HandlerFactory> mockHandlerFactory;
private Handler mockHandler;
private RangeConstruction mockRangeConstruction;
@Before
public void before() {
mockRangeConstruction = new RangeConstruction();
mockCameraProperties = mock(CameraProperties.class);
mockDartMessenger = mock(DartMessenger.class);
mockCaptureSession = mock(CameraCaptureSession.class);
mockPreviewRequestBuilder = mock(CaptureRequest.Builder.class);
mockHandlerThreadFactory = mockStatic(Camera.HandlerThreadFactory.class);
mockHandlerThread = mock(HandlerThread.class);
mockHandlerFactory = mockStatic(Camera.HandlerFactory.class);
mockHandler = mock(Handler.class);
final Activity mockActivity = mock(Activity.class);
final TextureRegistry.SurfaceTextureEntry mockFlutterTexture =
mock(TextureRegistry.SurfaceTextureEntry.class);
final String cameraName = "1";
final ResolutionPreset resolutionPreset = ResolutionPreset.high;
final boolean enableAudio = false;
when(mockCameraProperties.getCameraName()).thenReturn(cameraName);
mockHandlerFactory.when(() -> Camera.HandlerFactory.create(any())).thenReturn(mockHandler);
mockHandlerThreadFactory
.when(() -> Camera.HandlerThreadFactory.create(any()))
.thenReturn(mockHandlerThread);
// Use a wildcard, since `new Range<Integer>[] {...}`
// results in a 'Generic array creation' error.
@SuppressWarnings("unchecked")
final Range<Integer>[] mockRanges =
(Range<Integer>[]) new Range<?>[] {new Range<Integer>(10, 20)};
when(mockCameraProperties.getControlAutoExposureAvailableTargetFpsRanges())
.thenReturn(mockRanges);
final FpsRangeFeature fpsRangeFeature = new FpsRangeFeature(mockCameraProperties);
mockCameraFeatureFactory = new TestCameraFeatureFactory(fpsRangeFeature);
camera =
new Camera(
mockActivity,
mockFlutterTexture,
mockCameraFeatureFactory,
mockDartMessenger,
mockCameraProperties,
new Camera.VideoCaptureSettings(resolutionPreset, enableAudio));
final CamcorderProfile mockProfileLegacy = mock(CamcorderProfile.class);
mockProfileLegacy.videoFrameRate = 15;
when(camera.getRecordingProfileLegacy()).thenReturn(mockProfileLegacy);
TestUtils.setPrivateField(camera, "captureSession", mockCaptureSession);
TestUtils.setPrivateField(camera, "previewRequestBuilder", mockPreviewRequestBuilder);
}
@After
public void after() throws IOException {
SdkCapabilityChecker.SDK_VERSION = 0;
mockHandlerThreadFactory.close();
mockHandlerFactory.close();
mockRangeConstruction.close();
}
@Test
public void shouldNotImplementLifecycleObserverInterface() {
Class<Camera> cameraClass = Camera.class;
assertFalse(LifecycleObserver.class.isAssignableFrom(cameraClass));
}
@Test
public void shouldCreateCameraPluginAndSetAllFeatures() {
final Activity mockActivity = mock(Activity.class);
final TextureRegistry.SurfaceTextureEntry mockFlutterTexture =
mock(TextureRegistry.SurfaceTextureEntry.class);
final CameraFeatureFactory spyMockCameraFeatureFactory = spy(mockCameraFeatureFactory);
final String cameraName = "1";
final ResolutionPreset resolutionPreset = ResolutionPreset.high;
final boolean enableAudio = false;
when(mockCameraProperties.getCameraName()).thenReturn(cameraName);
SensorOrientationFeature mockSensorOrientationFeature = mock(SensorOrientationFeature.class);
when(spyMockCameraFeatureFactory.createSensorOrientationFeature(any(), any(), any()))
.thenReturn(mockSensorOrientationFeature);
Camera camera =
new Camera(
mockActivity,
mockFlutterTexture,
spyMockCameraFeatureFactory,
mockDartMessenger,
mockCameraProperties,
new Camera.VideoCaptureSettings(resolutionPreset, enableAudio));
verify(spyMockCameraFeatureFactory, times(1))
.createSensorOrientationFeature(mockCameraProperties, mockActivity, mockDartMessenger);
verify(spyMockCameraFeatureFactory, times(1))
.createAutoFocusFeature(mockCameraProperties, false);
verify(spyMockCameraFeatureFactory, times(1)).createExposureLockFeature(mockCameraProperties);
verify(spyMockCameraFeatureFactory, times(1))
.createExposurePointFeature(eq(mockCameraProperties), eq(mockSensorOrientationFeature));
verify(spyMockCameraFeatureFactory, times(1)).createExposureOffsetFeature(mockCameraProperties);
verify(spyMockCameraFeatureFactory, times(1)).createFlashFeature(mockCameraProperties);
verify(spyMockCameraFeatureFactory, times(1))
.createFocusPointFeature(eq(mockCameraProperties), eq(mockSensorOrientationFeature));
verify(spyMockCameraFeatureFactory, times(1)).createFpsRangeFeature(mockCameraProperties);
verify(spyMockCameraFeatureFactory, times(1)).createNoiseReductionFeature(mockCameraProperties);
verify(spyMockCameraFeatureFactory, times(1))
.createResolutionFeature(mockCameraProperties, resolutionPreset, cameraName);
verify(spyMockCameraFeatureFactory, times(1)).createZoomLevelFeature(mockCameraProperties);
assertNotNull("should create a camera", camera);
}
@Test
public void getDeviceOrientationManager() {
SensorOrientationFeature mockSensorOrientationFeature =
mockCameraFeatureFactory.createSensorOrientationFeature(mockCameraProperties, null, null);
DeviceOrientationManager mockDeviceOrientationManager = mock(DeviceOrientationManager.class);
when(mockSensorOrientationFeature.getDeviceOrientationManager())
.thenReturn(mockDeviceOrientationManager);
DeviceOrientationManager actualDeviceOrientationManager = camera.getDeviceOrientationManager();
verify(mockSensorOrientationFeature, times(1)).getDeviceOrientationManager();
assertEquals(mockDeviceOrientationManager, actualDeviceOrientationManager);
}
@Test
public void getExposureOffsetStepSize() {
ExposureOffsetFeature mockExposureOffsetFeature =
mockCameraFeatureFactory.createExposureOffsetFeature(mockCameraProperties);
double stepSize = 2.3;
when(mockExposureOffsetFeature.getExposureOffsetStepSize()).thenReturn(stepSize);
double actualSize = camera.getExposureOffsetStepSize();
verify(mockExposureOffsetFeature, times(1)).getExposureOffsetStepSize();
assertEquals(stepSize, actualSize, 0);
}
@Test
public void getMaxExposureOffset() {
ExposureOffsetFeature mockExposureOffsetFeature =
mockCameraFeatureFactory.createExposureOffsetFeature(mockCameraProperties);
double expectedMaxOffset = 42.0;
when(mockExposureOffsetFeature.getMaxExposureOffset()).thenReturn(expectedMaxOffset);
double actualMaxOffset = camera.getMaxExposureOffset();
verify(mockExposureOffsetFeature, times(1)).getMaxExposureOffset();
assertEquals(expectedMaxOffset, actualMaxOffset, 0);
}
@Test
public void getMinExposureOffset() {
ExposureOffsetFeature mockExposureOffsetFeature =
mockCameraFeatureFactory.createExposureOffsetFeature(mockCameraProperties);
double expectedMinOffset = 21.5;
when(mockExposureOffsetFeature.getMinExposureOffset()).thenReturn(21.5);
double actualMinOffset = camera.getMinExposureOffset();
verify(mockExposureOffsetFeature, times(1)).getMinExposureOffset();
assertEquals(expectedMinOffset, actualMinOffset, 0);
}
@Test
public void getMaxZoomLevel() {
ZoomLevelFeature mockZoomLevelFeature =
mockCameraFeatureFactory.createZoomLevelFeature(mockCameraProperties);
float expectedMaxZoomLevel = 4.2f;
when(mockZoomLevelFeature.getMaximumZoomLevel()).thenReturn(expectedMaxZoomLevel);
float actualMaxZoomLevel = camera.getMaxZoomLevel();
verify(mockZoomLevelFeature, times(1)).getMaximumZoomLevel();
assertEquals(expectedMaxZoomLevel, actualMaxZoomLevel, 0);
}
@Test
public void getMinZoomLevel() {
ZoomLevelFeature mockZoomLevelFeature =
mockCameraFeatureFactory.createZoomLevelFeature(mockCameraProperties);
float expectedMinZoomLevel = 4.2f;
when(mockZoomLevelFeature.getMinimumZoomLevel()).thenReturn(expectedMinZoomLevel);
float actualMinZoomLevel = camera.getMinZoomLevel();
verify(mockZoomLevelFeature, times(1)).getMinimumZoomLevel();
assertEquals(expectedMinZoomLevel, actualMinZoomLevel, 0);
}
@Test
public void setExposureMode_shouldUpdateExposureLockFeature() {
ExposureLockFeature mockExposureLockFeature =
mockCameraFeatureFactory.createExposureLockFeature(mockCameraProperties);
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
ExposureMode exposureMode = ExposureMode.locked;
camera.setExposureMode(mockResult, exposureMode);
verify(mockExposureLockFeature, times(1)).setValue(exposureMode);
verify(mockResult, never()).error(any(), any(), any());
verify(mockResult, times(1)).success(null);
}
@Test
public void setExposureMode_shouldUpdateBuilder() {
ExposureLockFeature mockExposureLockFeature =
mockCameraFeatureFactory.createExposureLockFeature(mockCameraProperties);
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
ExposureMode exposureMode = ExposureMode.locked;
camera.setExposureMode(mockResult, exposureMode);
verify(mockExposureLockFeature, times(1)).updateBuilder(any());
}
@Test
public void setExposureMode_shouldCallErrorOnResultOnCameraAccessException()
throws CameraAccessException {
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
ExposureMode exposureMode = ExposureMode.locked;
when(mockCaptureSession.setRepeatingRequest(any(), any(), any()))
.thenThrow(new CameraAccessException(0, ""));
camera.setExposureMode(mockResult, exposureMode);
verify(mockResult, never()).success(any());
verify(mockResult, times(1))
.error("setExposureModeFailed", "Could not set exposure mode.", null);
}
@Test
public void setExposurePoint_shouldUpdateExposurePointFeature() {
SensorOrientationFeature mockSensorOrientationFeature = mock(SensorOrientationFeature.class);
ExposurePointFeature mockExposurePointFeature =
mockCameraFeatureFactory.createExposurePointFeature(
mockCameraProperties, mockSensorOrientationFeature);
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
Point point = new Point(42d, 42d);
camera.setExposurePoint(mockResult, point);
verify(mockExposurePointFeature, times(1)).setValue(point);
verify(mockResult, never()).error(any(), any(), any());
verify(mockResult, times(1)).success(null);
}
@Test
public void setExposurePoint_shouldUpdateBuilder() {
SensorOrientationFeature mockSensorOrientationFeature = mock(SensorOrientationFeature.class);
ExposurePointFeature mockExposurePointFeature =
mockCameraFeatureFactory.createExposurePointFeature(
mockCameraProperties, mockSensorOrientationFeature);
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
Point point = new Point(42d, 42d);
camera.setExposurePoint(mockResult, point);
verify(mockExposurePointFeature, times(1)).updateBuilder(any());
}
@Test
public void setExposurePoint_shouldCallErrorOnResultOnCameraAccessException()
throws CameraAccessException {
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
Point point = new Point(42d, 42d);
when(mockCaptureSession.setRepeatingRequest(any(), any(), any()))
.thenThrow(new CameraAccessException(0, ""));
camera.setExposurePoint(mockResult, point);
verify(mockResult, never()).success(any());
verify(mockResult, times(1))
.error("setExposurePointFailed", "Could not set exposure point.", null);
}
@Test
public void setFlashMode_shouldUpdateFlashFeature() {
FlashFeature mockFlashFeature =
mockCameraFeatureFactory.createFlashFeature(mockCameraProperties);
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
FlashMode flashMode = FlashMode.always;
camera.setFlashMode(mockResult, flashMode);
verify(mockFlashFeature, times(1)).setValue(flashMode);
verify(mockResult, never()).error(any(), any(), any());
verify(mockResult, times(1)).success(null);
}
@Test
public void setFlashMode_shouldUpdateBuilder() {
FlashFeature mockFlashFeature =
mockCameraFeatureFactory.createFlashFeature(mockCameraProperties);
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
FlashMode flashMode = FlashMode.always;
camera.setFlashMode(mockResult, flashMode);
verify(mockFlashFeature, times(1)).updateBuilder(any());
}
@Test
public void setFlashMode_shouldCallErrorOnResultOnCameraAccessException()
throws CameraAccessException {
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
FlashMode flashMode = FlashMode.always;
when(mockCaptureSession.setRepeatingRequest(any(), any(), any()))
.thenThrow(new CameraAccessException(0, ""));
camera.setFlashMode(mockResult, flashMode);
verify(mockResult, never()).success(any());
verify(mockResult, times(1)).error("setFlashModeFailed", "Could not set flash mode.", null);
}
@Test
public void setFocusPoint_shouldUpdateFocusPointFeature() {
SensorOrientationFeature mockSensorOrientationFeature = mock(SensorOrientationFeature.class);
FocusPointFeature mockFocusPointFeature =
mockCameraFeatureFactory.createFocusPointFeature(
mockCameraProperties, mockSensorOrientationFeature);
AutoFocusFeature mockAutoFocusFeature =
mockCameraFeatureFactory.createAutoFocusFeature(mockCameraProperties, false);
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
Point point = new Point(42d, 42d);
when(mockAutoFocusFeature.getValue()).thenReturn(FocusMode.auto);
camera.setFocusPoint(mockResult, point);
verify(mockFocusPointFeature, times(1)).setValue(point);
verify(mockResult, never()).error(any(), any(), any());
verify(mockResult, times(1)).success(null);
}
@Test
public void setFocusPoint_shouldUpdateBuilder() {
SensorOrientationFeature mockSensorOrientationFeature = mock(SensorOrientationFeature.class);
FocusPointFeature mockFocusPointFeature =
mockCameraFeatureFactory.createFocusPointFeature(
mockCameraProperties, mockSensorOrientationFeature);
AutoFocusFeature mockAutoFocusFeature =
mockCameraFeatureFactory.createAutoFocusFeature(mockCameraProperties, false);
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
Point point = new Point(42d, 42d);
when(mockAutoFocusFeature.getValue()).thenReturn(FocusMode.auto);
camera.setFocusPoint(mockResult, point);
verify(mockFocusPointFeature, times(1)).updateBuilder(any());
}
@Test
public void setFocusPoint_shouldCallErrorOnResultOnCameraAccessException()
throws CameraAccessException {
AutoFocusFeature mockAutoFocusFeature =
mockCameraFeatureFactory.createAutoFocusFeature(mockCameraProperties, false);
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
Point point = new Point(42d, 42d);
when(mockAutoFocusFeature.getValue()).thenReturn(FocusMode.auto);
when(mockCaptureSession.setRepeatingRequest(any(), any(), any()))
.thenThrow(new CameraAccessException(0, ""));
camera.setFocusPoint(mockResult, point);
verify(mockResult, never()).success(any());
verify(mockResult, times(1)).error("setFocusPointFailed", "Could not set focus point.", null);
}
@Test
public void setZoomLevel_shouldUpdateZoomLevelFeature() throws CameraAccessException {
ZoomLevelFeature mockZoomLevelFeature =
mockCameraFeatureFactory.createZoomLevelFeature(mockCameraProperties);
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
float zoomLevel = 1.0f;
when(mockZoomLevelFeature.getValue()).thenReturn(zoomLevel);
when(mockZoomLevelFeature.getMinimumZoomLevel()).thenReturn(0f);
when(mockZoomLevelFeature.getMaximumZoomLevel()).thenReturn(2f);
camera.setZoomLevel(mockResult, zoomLevel);
verify(mockZoomLevelFeature, times(1)).setValue(zoomLevel);
verify(mockResult, never()).error(any(), any(), any());
verify(mockResult, times(1)).success(null);
}
@Test
public void setZoomLevel_shouldUpdateBuilder() throws CameraAccessException {
ZoomLevelFeature mockZoomLevelFeature =
mockCameraFeatureFactory.createZoomLevelFeature(mockCameraProperties);
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
float zoomLevel = 1.0f;
when(mockZoomLevelFeature.getValue()).thenReturn(zoomLevel);
when(mockZoomLevelFeature.getMinimumZoomLevel()).thenReturn(0f);
when(mockZoomLevelFeature.getMaximumZoomLevel()).thenReturn(2f);
camera.setZoomLevel(mockResult, zoomLevel);
verify(mockZoomLevelFeature, times(1)).updateBuilder(any());
}
@Test
public void setZoomLevel_shouldCallErrorOnResultOnCameraAccessException()
throws CameraAccessException {
ZoomLevelFeature mockZoomLevelFeature =
mockCameraFeatureFactory.createZoomLevelFeature(mockCameraProperties);
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
float zoomLevel = 1.0f;
when(mockZoomLevelFeature.getValue()).thenReturn(zoomLevel);
when(mockZoomLevelFeature.getMinimumZoomLevel()).thenReturn(0f);
when(mockZoomLevelFeature.getMaximumZoomLevel()).thenReturn(2f);
when(mockCaptureSession.setRepeatingRequest(any(), any(), any()))
.thenThrow(new CameraAccessException(0, ""));
camera.setZoomLevel(mockResult, zoomLevel);
verify(mockResult, never()).success(any());
verify(mockResult, times(1)).error("setZoomLevelFailed", "Could not set zoom level.", null);
}
@Test
public void pauseVideoRecording_shouldSendNullResultWhenNotRecording() {
TestUtils.setPrivateField(camera, "recordingVideo", false);
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
camera.pauseVideoRecording(mockResult);
verify(mockResult, times(1)).success(null);
verify(mockResult, never()).error(any(), any(), any());
}
@Test
public void pauseVideoRecording_shouldCallPauseWhenRecordingAndOnAPIN() {
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
MediaRecorder mockMediaRecorder = mock(MediaRecorder.class);
TestUtils.setPrivateField(camera, "mediaRecorder", mockMediaRecorder);
TestUtils.setPrivateField(camera, "recordingVideo", true);
SdkCapabilityChecker.SDK_VERSION = 24;
camera.pauseVideoRecording(mockResult);
verify(mockMediaRecorder, times(1)).pause();
verify(mockResult, times(1)).success(null);
verify(mockResult, never()).error(any(), any(), any());
}
@Test
public void pauseVideoRecording_shouldSendVideoRecordingFailedErrorWhenVersionCodeSmallerThenN() {
TestUtils.setPrivateField(camera, "recordingVideo", true);
SdkCapabilityChecker.SDK_VERSION = 23;
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
camera.pauseVideoRecording(mockResult);
verify(mockResult, times(1))
.error("videoRecordingFailed", "pauseVideoRecording requires Android API +24.", null);
verify(mockResult, never()).success(any());
}
@Test
public void
pauseVideoRecording_shouldSendVideoRecordingFailedErrorWhenMediaRecorderPauseThrowsIllegalStateException() {
MediaRecorder mockMediaRecorder = mock(MediaRecorder.class);
TestUtils.setPrivateField(camera, "mediaRecorder", mockMediaRecorder);
TestUtils.setPrivateField(camera, "recordingVideo", true);
SdkCapabilityChecker.SDK_VERSION = 24;
IllegalStateException expectedException = new IllegalStateException("Test error message");
doThrow(expectedException).when(mockMediaRecorder).pause();
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
camera.pauseVideoRecording(mockResult);
verify(mockResult, times(1)).error("videoRecordingFailed", "Test error message", null);
verify(mockResult, never()).success(any());
}
@Test
public void resumeVideoRecording_shouldSendNullResultWhenNotRecording() {
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
TestUtils.setPrivateField(camera, "recordingVideo", false);
camera.resumeVideoRecording(mockResult);
verify(mockResult, times(1)).success(null);
verify(mockResult, never()).error(any(), any(), any());
}
@Test
public void resumeVideoRecording_shouldCallPauseWhenRecordingAndOnAPIN() {
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
MediaRecorder mockMediaRecorder = mock(MediaRecorder.class);
TestUtils.setPrivateField(camera, "mediaRecorder", mockMediaRecorder);
TestUtils.setPrivateField(camera, "recordingVideo", true);
SdkCapabilityChecker.SDK_VERSION = 24;
camera.resumeVideoRecording(mockResult);
verify(mockMediaRecorder, times(1)).resume();
verify(mockResult, times(1)).success(null);
verify(mockResult, never()).error(any(), any(), any());
}
@Test
public void setDescriptionWhileRecording_errorsWhenUnsupported() {
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
MediaRecorder mockMediaRecorder = mock(MediaRecorder.class);
VideoRenderer mockVideoRenderer = mock(VideoRenderer.class);
TestUtils.setPrivateField(camera, "mediaRecorder", mockMediaRecorder);
TestUtils.setPrivateField(camera, "recordingVideo", true);
TestUtils.setPrivateField(camera, "videoRenderer", mockVideoRenderer);
SdkCapabilityChecker.SDK_VERSION = Build.VERSION_CODES.LOLLIPOP;
final CameraProperties newCameraProperties = mock(CameraProperties.class);
camera.setDescriptionWhileRecording(mockResult, newCameraProperties);
verify(mockResult, times(1))
.error(
eq("setDescriptionWhileRecordingFailed"),
eq("Device does not support switching the camera while recording"),
eq(null));
}
@Test
public void setDescriptionWhileRecording_succeedsWhenSupported() {
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
MediaRecorder mockMediaRecorder = mock(MediaRecorder.class);
VideoRenderer mockVideoRenderer = mock(VideoRenderer.class);
TestUtils.setPrivateField(camera, "mediaRecorder", mockMediaRecorder);
TestUtils.setPrivateField(camera, "recordingVideo", true);
TestUtils.setPrivateField(camera, "videoRenderer", mockVideoRenderer);
SdkCapabilityChecker.SDK_VERSION = Build.VERSION_CODES.O;
final CameraProperties newCameraProperties = mock(CameraProperties.class);
camera.setDescriptionWhileRecording(mockResult, newCameraProperties);
verify(mockResult, times(1)).success(null);
verify(mockResult, never()).error(any(), any(), any());
}
@Test
public void startPreview_shouldPullStreamFromVideoRenderer()
throws InterruptedException, CameraAccessException {
VideoRenderer mockVideoRenderer = mock(VideoRenderer.class);
ArrayList<CaptureRequest.Builder> mockRequestBuilders = new ArrayList<>();
mockRequestBuilders.add(mock(CaptureRequest.Builder.class));
SurfaceTexture mockSurfaceTexture = mock(SurfaceTexture.class);
Size mockSize = mock(Size.class);
TestUtils.setPrivateField(camera, "recordingVideo", true);
TestUtils.setPrivateField(camera, "videoRenderer", mockVideoRenderer);
CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders);
TestUtils.setPrivateField(camera, "cameraDevice", fakeCamera);
ImageReader mockPictureImageReader = mock(ImageReader.class);
TestUtils.setPrivateField(camera, "pictureImageReader", mockPictureImageReader);
TextureRegistry.SurfaceTextureEntry cameraFlutterTexture =
(TextureRegistry.SurfaceTextureEntry) TestUtils.getPrivateField(camera, "flutterTexture");
ResolutionFeature resolutionFeature =
(ResolutionFeature)
TestUtils.getPrivateField(mockCameraFeatureFactory, "mockResolutionFeature");
when(cameraFlutterTexture.surfaceTexture()).thenReturn(mockSurfaceTexture);
when(resolutionFeature.getPreviewSize()).thenReturn(mockSize);
camera.startPreview();
verify(mockVideoRenderer, times(1))
.getInputSurface(); // stream pulled from videoRenderer's surface.
}
@Test
public void startPreview_shouldPullStreamFromImageReader()
throws InterruptedException, CameraAccessException {
ArrayList<CaptureRequest.Builder> mockRequestBuilders = new ArrayList<>();
mockRequestBuilders.add(mock(CaptureRequest.Builder.class));
SurfaceTexture mockSurfaceTexture = mock(SurfaceTexture.class);
Size mockSize = mock(Size.class);
ImageReader mockImageReader = mock(ImageReader.class);
TestUtils.setPrivateField(camera, "recordingVideo", false);
TestUtils.setPrivateField(camera, "pictureImageReader", mockImageReader);
CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders);
TestUtils.setPrivateField(camera, "cameraDevice", fakeCamera);
TextureRegistry.SurfaceTextureEntry cameraFlutterTexture =
(TextureRegistry.SurfaceTextureEntry) TestUtils.getPrivateField(camera, "flutterTexture");
ResolutionFeature resolutionFeature =
(ResolutionFeature)
TestUtils.getPrivateField(mockCameraFeatureFactory, "mockResolutionFeature");
when(cameraFlutterTexture.surfaceTexture()).thenReturn(mockSurfaceTexture);
when(resolutionFeature.getPreviewSize()).thenReturn(mockSize);
when(mockImageReader.getSurface()).thenReturn(mock(Surface.class));
camera.startPreview();
verify(mockImageReader, times(2)) // we expect two calls to start regular preview.
.getSurface(); // stream pulled from regular imageReader's surface.
}
@Test
public void startPreview_shouldFlipRotation() throws InterruptedException, CameraAccessException {
VideoRenderer mockVideoRenderer = mock(VideoRenderer.class);
ArrayList<CaptureRequest.Builder> mockRequestBuilders = new ArrayList<>();
mockRequestBuilders.add(mock(CaptureRequest.Builder.class));
SurfaceTexture mockSurfaceTexture = mock(SurfaceTexture.class);
Size mockSize = mock(Size.class);
TestUtils.setPrivateField(camera, "recordingVideo", true);
TestUtils.setPrivateField(camera, "videoRenderer", mockVideoRenderer);
TestUtils.setPrivateField(camera, "initialCameraFacing", CameraMetadata.LENS_FACING_BACK);
CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders);
TestUtils.setPrivateField(camera, "cameraDevice", fakeCamera);
ImageReader mockPictureImageReader = mock(ImageReader.class);
TestUtils.setPrivateField(camera, "pictureImageReader", mockPictureImageReader);
TextureRegistry.SurfaceTextureEntry cameraFlutterTexture =
(TextureRegistry.SurfaceTextureEntry) TestUtils.getPrivateField(camera, "flutterTexture");
ResolutionFeature resolutionFeature =
(ResolutionFeature)
TestUtils.getPrivateField(mockCameraFeatureFactory, "mockResolutionFeature");
when(cameraFlutterTexture.surfaceTexture()).thenReturn(mockSurfaceTexture);
when(resolutionFeature.getPreviewSize()).thenReturn(mockSize);
when(mockCameraProperties.getLensFacing()).thenReturn(CameraMetadata.LENS_FACING_FRONT);
camera.startPreview();
verify(mockVideoRenderer, times(1)).setRotation(180);
}
@Test
public void startPreviewWithImageStream_shouldPullStreamsFromImageReaders()
throws InterruptedException, CameraAccessException {
ArrayList<CaptureRequest.Builder> mockRequestBuilders = new ArrayList<>();
mockRequestBuilders.add(mock(CaptureRequest.Builder.class));
SurfaceTexture mockSurfaceTexture = mock(SurfaceTexture.class);
Size mockSize = mock(Size.class);
ImageReader mockPictureImageReader = mock(ImageReader.class);
ImageStreamReader mockImageStreamReader = mock(ImageStreamReader.class);
TestUtils.setPrivateField(camera, "recordingVideo", false);
TestUtils.setPrivateField(camera, "pictureImageReader", mockPictureImageReader);
CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders);
TestUtils.setPrivateField(camera, "cameraDevice", fakeCamera);
camera.imageStreamReader = mockImageStreamReader;
TextureRegistry.SurfaceTextureEntry cameraFlutterTexture =
(TextureRegistry.SurfaceTextureEntry) TestUtils.getPrivateField(camera, "flutterTexture");
ResolutionFeature resolutionFeature =
(ResolutionFeature)
TestUtils.getPrivateField(mockCameraFeatureFactory, "mockResolutionFeature");
when(cameraFlutterTexture.surfaceTexture()).thenReturn(mockSurfaceTexture);
when(resolutionFeature.getPreviewSize()).thenReturn(mockSize);
camera.startPreviewWithImageStream(mock(EventChannel.class));
verify(mockImageStreamReader, times(1))
.getSurface(); // stream pulled from image streaming imageReader's surface.
verify(
mockPictureImageReader,
times(2)) // we expect one call to start the capture, one to create the capture session.
.getSurface(); // stream pulled from regular imageReader's surface.
}
@Test
public void setDescriptionWhileRecording_shouldErrorWhenNotRecording() {
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
TestUtils.setPrivateField(camera, "recordingVideo", false);
final CameraProperties newCameraProperties = mock(CameraProperties.class);
camera.setDescriptionWhileRecording(mockResult, newCameraProperties);
verify(mockResult, times(1))
.error("setDescriptionWhileRecordingFailed", "Device was not recording", null);
verify(mockResult, never()).success(any());
}
@Test
public void
resumeVideoRecording_shouldSendVideoRecordingFailedErrorWhenVersionCodeSmallerThanN() {
TestUtils.setPrivateField(camera, "recordingVideo", true);
SdkCapabilityChecker.SDK_VERSION = 23;
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
camera.resumeVideoRecording(mockResult);
verify(mockResult, times(1))
.error("videoRecordingFailed", "resumeVideoRecording requires Android API +24.", null);
verify(mockResult, never()).success(any());
}
@Test
public void
resumeVideoRecording_shouldSendVideoRecordingFailedErrorWhenMediaRecorderPauseThrowsIllegalStateException() {
MediaRecorder mockMediaRecorder = mock(MediaRecorder.class);
TestUtils.setPrivateField(camera, "mediaRecorder", mockMediaRecorder);
TestUtils.setPrivateField(camera, "recordingVideo", true);
SdkCapabilityChecker.SDK_VERSION = 24;
IllegalStateException expectedException = new IllegalStateException("Test error message");
doThrow(expectedException).when(mockMediaRecorder).resume();
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
camera.resumeVideoRecording(mockResult);
verify(mockResult, times(1)).error("videoRecordingFailed", "Test error message", null);
verify(mockResult, never()).success(any());
}
@Test
public void setFocusMode_shouldUpdateAutoFocusFeature() {
AutoFocusFeature mockAutoFocusFeature =
mockCameraFeatureFactory.createAutoFocusFeature(mockCameraProperties, false);
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
camera.setFocusMode(mockResult, FocusMode.auto);
verify(mockAutoFocusFeature, times(1)).setValue(FocusMode.auto);
verify(mockResult, never()).error(any(), any(), any());
verify(mockResult, times(1)).success(null);
}
@Test
public void setFocusMode_shouldUpdateBuilder() {
AutoFocusFeature mockAutoFocusFeature =
mockCameraFeatureFactory.createAutoFocusFeature(mockCameraProperties, false);
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
camera.setFocusMode(mockResult, FocusMode.auto);
verify(mockAutoFocusFeature, times(1)).updateBuilder(any());
}
@Test
public void setFocusMode_shouldUnlockAutoFocusForAutoMode() {
camera.setFocusMode(mock(MethodChannel.Result.class), FocusMode.auto);
verify(mockPreviewRequestBuilder, times(1))
.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL);
verify(mockPreviewRequestBuilder, times(1))
.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_IDLE);
}
@Test
public void setFocusMode_shouldSkipUnlockAutoFocusWhenNullCaptureSession() {
TestUtils.setPrivateField(camera, "captureSession", null);
camera.setFocusMode(mock(MethodChannel.Result.class), FocusMode.auto);
verify(mockPreviewRequestBuilder, never())
.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL);
verify(mockPreviewRequestBuilder, never())
.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_IDLE);
}
@Test
public void setFocusMode_shouldSendErrorEventOnUnlockAutoFocusCameraAccessException()
throws CameraAccessException {
when(mockCaptureSession.capture(any(), any(), any()))
.thenThrow(new CameraAccessException(0, ""));
camera.setFocusMode(mock(MethodChannel.Result.class), FocusMode.auto);
verify(mockDartMessenger, times(1)).sendCameraErrorEvent(any());
}
@Test
public void startVideoRecording_shouldPullStreamsFromMediaRecorderAndImageReader()
throws InterruptedException, IOException, CameraAccessException {
Camera cameraSpy = spy(camera);
ArrayList<CaptureRequest.Builder> mockRequestBuilders = new ArrayList<>();
mockRequestBuilders.add(mock(CaptureRequest.Builder.class));
SurfaceTexture mockSurfaceTexture = mock(SurfaceTexture.class);
Size mockSize = mock(Size.class);
MediaRecorder mockMediaRecorder = mock(MediaRecorder.class);
ImageReader mockPictureImageReader = mock(ImageReader.class);
TestUtils.setPrivateField(cameraSpy, "mediaRecorder", mockMediaRecorder);
TestUtils.setPrivateField(cameraSpy, "recordingVideo", false);
TestUtils.setPrivateField(cameraSpy, "pictureImageReader", mockPictureImageReader);
CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders);
TestUtils.setPrivateField(cameraSpy, "cameraDevice", fakeCamera);
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
TextureRegistry.SurfaceTextureEntry cameraFlutterTexture =
(TextureRegistry.SurfaceTextureEntry)
TestUtils.getPrivateField(cameraSpy, "flutterTexture");
ResolutionFeature resolutionFeature =
(ResolutionFeature)
TestUtils.getPrivateField(mockCameraFeatureFactory, "mockResolutionFeature");
when(cameraFlutterTexture.surfaceTexture()).thenReturn(mockSurfaceTexture);
when(resolutionFeature.getPreviewSize()).thenReturn(mockSize);
doNothing().when(cameraSpy).prepareRecording(mockResult);
cameraSpy.startVideoRecording(mockResult, null);
verify(mockMediaRecorder, times(1))
.getSurface(); // stream pulled from media recorder's surface.
verify(
mockPictureImageReader,
times(2)) // we expect one call to start the capture, one to create the capture session.
.getSurface(); // stream pulled from image streaming imageReader's surface.
}
@Test
public void setFocusMode_shouldLockAutoFocusForLockedMode() throws CameraAccessException {
camera.setFocusMode(mock(MethodChannel.Result.class), FocusMode.locked);
verify(mockPreviewRequestBuilder, times(1))
.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START);
verify(mockCaptureSession, times(1)).capture(any(), any(), any());
verify(mockCaptureSession, times(1)).setRepeatingRequest(any(), any(), any());
}
@Test
public void setFocusMode_shouldSkipLockAutoFocusWhenNullCaptureSession() {
TestUtils.setPrivateField(camera, "captureSession", null);
camera.setFocusMode(mock(MethodChannel.Result.class), FocusMode.locked);
verify(mockPreviewRequestBuilder, never())
.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START);
}
@Test
public void setFocusMode_shouldSendErrorEventOnLockAutoFocusCameraAccessException()
throws CameraAccessException {
when(mockCaptureSession.capture(any(), any(), any()))
.thenThrow(new CameraAccessException(0, ""));
camera.setFocusMode(mock(MethodChannel.Result.class), FocusMode.locked);
verify(mockDartMessenger, times(1)).sendCameraErrorEvent(any());
}
@Test
public void setFocusMode_shouldCallErrorOnResultOnCameraAccessException()
throws CameraAccessException {
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
when(mockCaptureSession.setRepeatingRequest(any(), any(), any()))
.thenThrow(new CameraAccessException(0, ""));
camera.setFocusMode(mockResult, FocusMode.locked);
verify(mockResult, never()).success(any());
verify(mockResult, times(1))
.error("setFocusModeFailed", "Error setting focus mode: null", null);
}
@Test
public void setExposureOffset_shouldUpdateExposureOffsetFeature() {
ExposureOffsetFeature mockExposureOffsetFeature =
mockCameraFeatureFactory.createExposureOffsetFeature(mockCameraProperties);
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
when(mockExposureOffsetFeature.getValue()).thenReturn(1.0);
camera.setExposureOffset(mockResult, 1.0);
verify(mockExposureOffsetFeature, times(1)).setValue(1.0);
verify(mockResult, never()).error(any(), any(), any());
verify(mockResult, times(1)).success(1.0);
}
@Test
public void setExposureOffset_shouldAndUpdateBuilder() {
ExposureOffsetFeature mockExposureOffsetFeature =
mockCameraFeatureFactory.createExposureOffsetFeature(mockCameraProperties);
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
camera.setExposureOffset(mockResult, 1.0);
verify(mockExposureOffsetFeature, times(1)).updateBuilder(any());
}
@Test
public void setExposureOffset_shouldCallErrorOnResultOnCameraAccessException()
throws CameraAccessException {
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
when(mockCaptureSession.setRepeatingRequest(any(), any(), any()))
.thenThrow(new CameraAccessException(0, ""));
camera.setExposureOffset(mockResult, 1.0);
verify(mockResult, never()).success(any());
verify(mockResult, times(1))
.error("setExposureOffsetFailed", "Could not set exposure offset.", null);
}
@Test
public void lockCaptureOrientation_shouldLockCaptureOrientation() {
final Activity mockActivity = mock(Activity.class);
SensorOrientationFeature mockSensorOrientationFeature =
mockCameraFeatureFactory.createSensorOrientationFeature(
mockCameraProperties, mockActivity, mockDartMessenger);
camera.lockCaptureOrientation(PlatformChannel.DeviceOrientation.PORTRAIT_UP);
verify(mockSensorOrientationFeature, times(1))
.lockCaptureOrientation(PlatformChannel.DeviceOrientation.PORTRAIT_UP);
}
@Test
public void unlockCaptureOrientation_shouldUnlockCaptureOrientation() {
final Activity mockActivity = mock(Activity.class);
SensorOrientationFeature mockSensorOrientationFeature =
mockCameraFeatureFactory.createSensorOrientationFeature(
mockCameraProperties, mockActivity, mockDartMessenger);
camera.unlockCaptureOrientation();
verify(mockSensorOrientationFeature, times(1)).unlockCaptureOrientation();
}
@Test
public void pausePreview_shouldPausePreview() throws CameraAccessException {
camera.pausePreview();
assertEquals(TestUtils.getPrivateField(camera, "pausedPreview"), true);
verify(mockCaptureSession, times(1)).stopRepeating();
}
@Test
public void resumePreview_shouldResumePreview() throws CameraAccessException {
camera.resumePreview();
assertEquals(TestUtils.getPrivateField(camera, "pausedPreview"), false);
verify(mockCaptureSession, times(1)).setRepeatingRequest(any(), any(), any());
}
@Test
public void resumePreview_shouldSendErrorEventOnCameraAccessException()
throws CameraAccessException {
when(mockCaptureSession.setRepeatingRequest(any(), any(), any()))
.thenThrow(new CameraAccessException(0));
camera.resumePreview();
verify(mockDartMessenger, times(1)).sendCameraErrorEvent(any());
}
@Test
public void startBackgroundThread_shouldStartNewThread() {
camera.startBackgroundThread();
verify(mockHandlerThread, times(1)).start();
assertEquals(mockHandler, TestUtils.getPrivateField(camera, "backgroundHandler"));
}
@Test
public void startBackgroundThread_shouldNotStartNewThreadWhenAlreadyCreated() {
camera.startBackgroundThread();
camera.startBackgroundThread();
verify(mockHandlerThread, times(1)).start();
}
@Test
public void stopBackgroundThread_quitsSafely() throws InterruptedException {
camera.startBackgroundThread();
camera.stopBackgroundThread();
verify(mockHandlerThread).quitSafely();
verify(mockHandlerThread, never()).join();
}
@Test
public void onConverge_shouldTakePictureWithoutAbortingSession() throws CameraAccessException {
ArrayList<CaptureRequest.Builder> mockRequestBuilders = new ArrayList<>();
mockRequestBuilders.add(mock(CaptureRequest.Builder.class));
CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders);
// Stub out other features used by the flow.
TestUtils.setPrivateField(camera, "cameraDevice", fakeCamera);
TestUtils.setPrivateField(camera, "pictureImageReader", mock(ImageReader.class));
SensorOrientationFeature mockSensorOrientationFeature =
mockCameraFeatureFactory.createSensorOrientationFeature(mockCameraProperties, null, null);
DeviceOrientationManager mockDeviceOrientationManager = mock(DeviceOrientationManager.class);
when(mockSensorOrientationFeature.getDeviceOrientationManager())
.thenReturn(mockDeviceOrientationManager);
// Simulate a post-precapture flow.
camera.onConverged();
// A picture should be taken.
verify(mockCaptureSession, times(1)).capture(any(), any(), any());
// The session shuold not be aborted as part of this flow, as this breaks capture on some
// devices, and causes delays on others.
verify(mockCaptureSession, never()).abortCaptures();
}
@Test
public void createCaptureSession_doesNotCloseCaptureSession() throws CameraAccessException {
Surface mockSurface = mock(Surface.class);
SurfaceTexture mockSurfaceTexture = mock(SurfaceTexture.class);
ResolutionFeature mockResolutionFeature = mock(ResolutionFeature.class);
Size mockSize = mock(Size.class);
ArrayList<CaptureRequest.Builder> mockRequestBuilders = new ArrayList<>();
mockRequestBuilders.add(mock(CaptureRequest.Builder.class));
CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders);
TestUtils.setPrivateField(camera, "cameraDevice", fakeCamera);
TextureRegistry.SurfaceTextureEntry cameraFlutterTexture =
(TextureRegistry.SurfaceTextureEntry) TestUtils.getPrivateField(camera, "flutterTexture");
CameraFeatures cameraFeatures =
(CameraFeatures) TestUtils.getPrivateField(camera, "cameraFeatures");
ResolutionFeature resolutionFeature =
(ResolutionFeature)
TestUtils.getPrivateField(mockCameraFeatureFactory, "mockResolutionFeature");
when(cameraFlutterTexture.surfaceTexture()).thenReturn(mockSurfaceTexture);
when(resolutionFeature.getPreviewSize()).thenReturn(mockSize);
camera.createCaptureSession(CameraDevice.TEMPLATE_PREVIEW, mockSurface);
verify(mockCaptureSession, never()).close();
}
@Test
public void createCaptureSession_shouldNotAddPictureImageSurfaceToPreviewRequest()
throws CameraAccessException {
Surface mockSurface = mock(Surface.class);
Surface mockSecondarySurface = mock(Surface.class);
SurfaceTexture mockSurfaceTexture = mock(SurfaceTexture.class);
ResolutionFeature mockResolutionFeature = mock(ResolutionFeature.class);
Size mockSize = mock(Size.class);
ArrayList<CaptureRequest.Builder> mockRequestBuilders = new ArrayList<>();
mockRequestBuilders.add(mock(CaptureRequest.Builder.class));
CameraDeviceWrapper fakeCamera = spy(new FakeCameraDeviceWrapper(mockRequestBuilders));
ImageReader mockPictureImageReader = mock(ImageReader.class);
TestUtils.setPrivateField(camera, "cameraDevice", fakeCamera);
TestUtils.setPrivateField(camera, "pictureImageReader", mockPictureImageReader);
CaptureRequest.Builder mockPreviewRequestBuilder = mock(CaptureRequest.Builder.class);
TextureRegistry.SurfaceTextureEntry cameraFlutterTexture =
(TextureRegistry.SurfaceTextureEntry) TestUtils.getPrivateField(camera, "flutterTexture");
CameraFeatures cameraFeatures =
(CameraFeatures) TestUtils.getPrivateField(camera, "cameraFeatures");
ResolutionFeature resolutionFeature =
(ResolutionFeature)
TestUtils.getPrivateField(mockCameraFeatureFactory, "mockResolutionFeature");
when(cameraFlutterTexture.surfaceTexture()).thenReturn(mockSurfaceTexture);
when(resolutionFeature.getPreviewSize()).thenReturn(mockSize);
when(fakeCamera.createCaptureRequest(anyInt())).thenReturn(mockPreviewRequestBuilder);
when(mockPictureImageReader.getSurface()).thenReturn(mockSurface);
// Test with preview template.
camera.createCaptureSession(CameraDevice.TEMPLATE_PREVIEW, mockSurface, mockSecondarySurface);
verify(mockPreviewRequestBuilder, times(0)).addTarget(mockSurface);
// Test with non-preview template.
camera.createCaptureSession(CameraDevice.TEMPLATE_RECORD, mockSurface, mockSecondarySurface);
verify(mockPreviewRequestBuilder, times(0)).addTarget(mockSurface);
verify(mockPreviewRequestBuilder).addTarget(mockSecondarySurface);
}
@Test
public void close_doesCloseCaptureSessionWhenCameraDeviceNull() {
camera.close();
verify(mockCaptureSession).close();
}
@Test
public void close_doesNotCloseCaptureSessionWhenCameraDeviceNonNull() {
ArrayList<CaptureRequest.Builder> mockRequestBuilders = new ArrayList<>();
mockRequestBuilders.add(mock(CaptureRequest.Builder.class));
CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders);
TestUtils.setPrivateField(camera, "cameraDevice", fakeCamera);
camera.close();
verify(mockCaptureSession, never()).close();
}
@Test
public void startVideoRecording_shouldApplySettingsToMediaRecorder()
throws InterruptedException, IOException, CameraAccessException {
final Activity mockActivity = mock(Activity.class);
final TextureRegistry.SurfaceTextureEntry mockFlutterTexture =
mock(TextureRegistry.SurfaceTextureEntry.class);
final String cameraName = "1";
final ResolutionPreset resolutionPreset = ResolutionPreset.high;
final boolean enableAudio = true;
//region These parameters should be set in android MediaRecorder.
final int fps = 15;
final int videoBitrate = 200000;
final int audioBitrate = 32000;
//endregion
when(mockCameraProperties.getCameraName()).thenReturn(cameraName);
final Camera.VideoCaptureSettings parameters =
new Camera.VideoCaptureSettings(
resolutionPreset, enableAudio, fps, videoBitrate, audioBitrate);
// Use a wildcard, since `new Range<Integer>[] {...}`
// results in a 'Generic array creation' error.
@SuppressWarnings("unchecked")
final Range<Integer>[] mockRanges =
(Range<Integer>[]) new Range<?>[] {new Range<Integer>(10, 20)};
when(mockCameraProperties.getControlAutoExposureAvailableTargetFpsRanges())
.thenReturn(mockRanges);
final Context mockApplicationContext = mock(Context.class);
when(mockActivity.getApplicationContext()).thenReturn(mockApplicationContext);
try (final MockedStatic<File> mockFile = mockStatic(File.class);
final MockedConstruction<MediaRecorder> mockMediaRecorder =
Mockito.mockConstruction(MediaRecorder.class)) {
assertNotNull(mockMediaRecorder);
mockFile
.when(() -> File.createTempFile(any(), any(), any()))
.thenReturn(new File("/tmp/file.mp4"));
final FpsRangeFeature fpsRangeFeature = new FpsRangeFeature(mockCameraProperties);
final Camera camera =
spy(
new Camera(
mockActivity,
mockFlutterTexture,
mockCameraFeatureFactory,
mockDartMessenger,
mockCameraProperties,
parameters));
final CamcorderProfile mockProfileLegacy = mock(CamcorderProfile.class);
mockProfileLegacy.videoFrameRate = fps;
when(camera.getRecordingProfileLegacy()).thenReturn(mockProfileLegacy);
final SensorOrientationFeature mockSensorOrientationFeature =
mockCameraFeatureFactory.createSensorOrientationFeature(mockCameraProperties, null, null);
DeviceOrientationManager mockDeviceOrientationManager = mock(DeviceOrientationManager.class);
when(mockSensorOrientationFeature.getDeviceOrientationManager())
.thenReturn(mockDeviceOrientationManager);
TestUtils.setPrivateField(camera, "captureSession", mockCaptureSession);
TestUtils.setPrivateField(camera, "previewRequestBuilder", mockPreviewRequestBuilder);
final ArrayList<CaptureRequest.Builder> mockRequestBuilders = new ArrayList<>();
CaptureRequest.Builder mockRequestBuilder = mock(CaptureRequest.Builder.class);
mockRequestBuilders.add(mockRequestBuilder);
final SurfaceTexture mockSurfaceTexture = mock(SurfaceTexture.class);
final Size mockSize = mock(Size.class);
final ImageReader mockPictureImageReader = mock(ImageReader.class);
TestUtils.setPrivateField(camera, "pictureImageReader", mockPictureImageReader);
final CameraDeviceWrapper fakeCamera =
new FakeCameraDeviceWrapper(mockRequestBuilders, mockCaptureSession);
TestUtils.setPrivateField(camera, "cameraDevice", fakeCamera);
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
TextureRegistry.SurfaceTextureEntry cameraFlutterTexture =
(TextureRegistry.SurfaceTextureEntry) TestUtils.getPrivateField(camera, "flutterTexture");
ResolutionFeature resolutionFeature =
(ResolutionFeature)
TestUtils.getPrivateField(mockCameraFeatureFactory, "mockResolutionFeature");
assertNotNull(cameraFlutterTexture);
when(cameraFlutterTexture.surfaceTexture()).thenReturn(mockSurfaceTexture);
assertNotNull(resolutionFeature);
when(resolutionFeature.getPreviewSize()).thenReturn(mockSize);
camera.startVideoRecording(mockResult, null);
//region Check that FPS parameter affects AE range at which the camera captures frames.
assertEquals(camera.cameraFeatures.getFpsRange().getValue().getLower(), Integer.valueOf(fps));
assertEquals(camera.cameraFeatures.getFpsRange().getValue().getUpper(), Integer.valueOf(fps));
verify(mockRequestBuilder)
.set(
eq(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE),
argThat(
(Range<Integer> range) -> range.getLower() == fps && range.getUpper() == fps));
//endregion
final MediaRecorder recorder =
(MediaRecorder) TestUtils.getPrivateField(camera, "mediaRecorder");
//region Check that parameters affects movies, written by MediaRecorder.
verify(recorder).setVideoFrameRate(fps);
verify(recorder).setAudioEncodingBitRate(audioBitrate);
verify(recorder).setVideoEncodingBitRate(videoBitrate);
//endregion
}
}
@Test
public void pausePreview_doesNotCallStopRepeatingWhenCameraClosed() throws CameraAccessException {
ArrayList<CaptureRequest.Builder> mockRequestBuilders = new ArrayList<>();
mockRequestBuilders.add(mock(CaptureRequest.Builder.class));
CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders);
TestUtils.setPrivateField(camera, "cameraDevice", fakeCamera);
camera.close();
camera.pausePreview();
verify(mockCaptureSession, never()).stopRepeating();
}
/// Allow to use `new android.util.Range(Integer, Integer)`
private static class RangeConstruction implements Closeable {
final Map<Range<Integer>, Integer> lowers = new HashMap<>();
final Map<Range<Integer>, Integer> uppers = new HashMap<>();
@SuppressWarnings({"rawtypes"})
final MockedConstruction<Range> rangeMockedConstruction;
@SuppressWarnings({"unchecked"})
public RangeConstruction() {
this.rangeMockedConstruction =
Mockito.mockConstruction(
Range.class,
(mock, context) -> {
int lower = (int) context.arguments().get(0);
int upper = (int) context.arguments().get(1);
lowers.put((Range<Integer>) mock, lower);
uppers.put((Range<Integer>) mock, upper);
when(((Range<Integer>) mock).getUpper())
.thenReturn(lowers.getOrDefault((Range<Integer>) mock, 15));
when(((Range<Integer>) mock).getLower())
.thenReturn(uppers.getOrDefault((Range<Integer>) mock, 15));
when(mock.toString())
.thenReturn(
String.format(
"mocked [%s, %s]",
lowers.getOrDefault(mock, 15), uppers.getOrDefault(mock, 15)));
});
}
@Override
public void close() throws IOException {
rangeMockedConstruction.close();
}
}
private static class TestCameraFeatureFactory implements CameraFeatureFactory {
private final AutoFocusFeature mockAutoFocusFeature;
private final ExposureLockFeature mockExposureLockFeature;
private final ExposureOffsetFeature mockExposureOffsetFeature;
private final ExposurePointFeature mockExposurePointFeature;
private final FlashFeature mockFlashFeature;
private final FocusPointFeature mockFocusPointFeature;
private final FpsRangeFeature mockFpsRangeFeature;
private final NoiseReductionFeature mockNoiseReductionFeature;
private final ResolutionFeature mockResolutionFeature;
private final SensorOrientationFeature mockSensorOrientationFeature;
private final ZoomLevelFeature mockZoomLevelFeature;
public TestCameraFeatureFactory(FpsRangeFeature fpsRangeFeature) {
this.mockAutoFocusFeature = mock(AutoFocusFeature.class);
this.mockExposureLockFeature = mock(ExposureLockFeature.class);
this.mockExposureOffsetFeature = mock(ExposureOffsetFeature.class);
this.mockExposurePointFeature = mock(ExposurePointFeature.class);
this.mockFlashFeature = mock(FlashFeature.class);
this.mockFocusPointFeature = mock(FocusPointFeature.class);
this.mockFpsRangeFeature = fpsRangeFeature;
this.mockNoiseReductionFeature = mock(NoiseReductionFeature.class);
this.mockResolutionFeature = mock(ResolutionFeature.class);
this.mockSensorOrientationFeature = mock(SensorOrientationFeature.class);
this.mockZoomLevelFeature = mock(ZoomLevelFeature.class);
}
@Override
public AutoFocusFeature createAutoFocusFeature(
@NonNull CameraProperties cameraProperties, boolean recordingVideo) {
return mockAutoFocusFeature;
}
@Override
public ExposureLockFeature createExposureLockFeature(
@NonNull CameraProperties cameraProperties) {
return mockExposureLockFeature;
}
@Override
public ExposureOffsetFeature createExposureOffsetFeature(
@NonNull CameraProperties cameraProperties) {
return mockExposureOffsetFeature;
}
@Override
public FlashFeature createFlashFeature(@NonNull CameraProperties cameraProperties) {
return mockFlashFeature;
}
@Override
public ResolutionFeature createResolutionFeature(
@NonNull CameraProperties cameraProperties,
ResolutionPreset initialSetting,
String cameraName) {
return mockResolutionFeature;
}
@Override
public FocusPointFeature createFocusPointFeature(
@NonNull CameraProperties cameraProperties,
@NonNull SensorOrientationFeature sensorOrienttionFeature) {
return mockFocusPointFeature;
}
@Override
public FpsRangeFeature createFpsRangeFeature(@NonNull CameraProperties cameraProperties) {
return mockFpsRangeFeature;
}
@Override
public SensorOrientationFeature createSensorOrientationFeature(
@NonNull CameraProperties cameraProperties,
@NonNull Activity activity,
@NonNull DartMessenger dartMessenger) {
return mockSensorOrientationFeature;
}
@Override
public ZoomLevelFeature createZoomLevelFeature(@NonNull CameraProperties cameraProperties) {
return mockZoomLevelFeature;
}
@Override
public ExposurePointFeature createExposurePointFeature(
@NonNull CameraProperties cameraProperties,
@NonNull SensorOrientationFeature sensorOrientationFeature) {
return mockExposurePointFeature;
}
@Override
public NoiseReductionFeature createNoiseReductionFeature(
@NonNull CameraProperties cameraProperties) {
return mockNoiseReductionFeature;
}
}
}