Skip to content
Merged
Prev Previous commit
Next Next commit
Add tests
  • Loading branch information
camsim99 committed Feb 14, 2024
commit ab46d0bb46f238b14af8f7594b52ca076ca8088f
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ public final class CameraAndroidCameraxPlugin implements FlutterPlugin, Activity
@VisibleForTesting @Nullable public ImageCaptureHostApiImpl imageCaptureHostApiImpl;
@VisibleForTesting @Nullable public CameraControlHostApiImpl cameraControlHostApiImpl;
@VisibleForTesting @Nullable public SystemServicesHostApiImpl systemServicesHostApiImpl;
@VisibleForTesting @Nullable public Camera2CameraControlHostApiImpl camera2CameraControlHostApiImpl;

@VisibleForTesting @Nullable
public Camera2CameraControlHostApiImpl camera2CameraControlHostApiImpl;

@VisibleForTesting
public @Nullable DeviceOrientationManagerHostApiImpl deviceOrientationManagerHostApiImpl;
Expand Down Expand Up @@ -121,8 +123,10 @@ public void setUp(
new CameraControlHostApiImpl(binaryMessenger, instanceManager, context);
GeneratedCameraXLibrary.CameraControlHostApi.setup(binaryMessenger, cameraControlHostApiImpl);
camera2CameraControlHostApiImpl = new Camera2CameraControlHostApiImpl(instanceManager, context);
GeneratedCameraXLibrary.Camera2CameraControlHostApi.setup(binaryMessenger, camera2CameraControlHostApiImpl);
GeneratedCameraXLibrary.CaptureRequestOptionsHostApi.setup(binaryMessenger, new CaptureRequestOptionsHostApiImpl(instanceManager));
GeneratedCameraXLibrary.Camera2CameraControlHostApi.setup(
binaryMessenger, camera2CameraControlHostApiImpl);
GeneratedCameraXLibrary.CaptureRequestOptionsHostApi.setup(
binaryMessenger, new CaptureRequestOptionsHostApiImpl(instanceManager));
}

@Override
Expand Down Expand Up @@ -214,7 +218,7 @@ public void updateContext(@NonNull Context context) {
if (cameraControlHostApiImpl != null) {
cameraControlHostApiImpl.setContext(context);
}
if (camera2CameraControlHostApiImpl != null) {
if (camera2CameraControlHostApiImpl != null) {
camera2CameraControlHostApiImpl.setContext(context);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,7 @@ public void create(@NonNull Long identifier, @NonNull Map<Long, Object> options)
new HashMap<CaptureRequestKeySupportedType, Object>();
for (Map.Entry<Long, Object> option : options.entrySet()) {
Integer index = ((Number) option.getKey()).intValue();
decodedOptions.put(
CaptureRequestKeySupportedType.values()[index], option.getValue());
decodedOptions.put(CaptureRequestKeySupportedType.values()[index], option.getValue());
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a fix for a casting error I got.

}
instanceManager.addDartCreatedInstance(proxy.create(decodedOptions), identifier);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@ public void onAttachedToActivity_setsActivityAsNeededAndPermissionsRegistry() {
mock(ImageAnalysisHostApiImpl.class);
final CameraControlHostApiImpl mockCameraControlHostApiImpl =
mock(CameraControlHostApiImpl.class);
final Camera2CameraControlHostApiImpl mockCamera2CameraControlHostApiImpl =
mock(Camera2CameraControlHostApiImpl.class);

when(flutterPluginBinding.getApplicationContext()).thenReturn(mockContext);

Expand All @@ -172,6 +174,7 @@ public void onAttachedToActivity_setsActivityAsNeededAndPermissionsRegistry() {
plugin.imageAnalysisHostApiImpl = mockImageAnalysisHostApiImpl;
plugin.cameraControlHostApiImpl = mockCameraControlHostApiImpl;
plugin.liveDataHostApiImpl = mock(LiveDataHostApiImpl.class);
plugin.camera2CameraControlHostApiImpl = mockCamera2CameraControlHostApiImpl;

plugin.onAttachedToEngine(flutterPluginBinding);
plugin.onDetachedFromActivityForConfigChanges();
Expand All @@ -183,6 +186,7 @@ public void onAttachedToActivity_setsActivityAsNeededAndPermissionsRegistry() {
verify(mockImageCaptureHostApiImpl).setContext(mockContext);
verify(mockImageAnalysisHostApiImpl).setContext(mockContext);
verify(mockCameraControlHostApiImpl).setContext(mockContext);
verify(mockCamera2CameraControlHostApiImpl).setContext(mockContext);
}

@Test
Expand Down Expand Up @@ -251,6 +255,8 @@ public void onReattachedToActivityForConfigChanges_setsActivityAndPermissionsReg
mock(CameraControlHostApiImpl.class);
final DeviceOrientationManagerHostApiImpl mockDeviceOrientationManagerHostApiImpl =
mock(DeviceOrientationManagerHostApiImpl.class);
final Camera2CameraControlHostApiImpl mockCamera2CameraControlHostApiImpl =
mock(Camera2CameraControlHostApiImpl.class);
final ArgumentCaptor<PermissionsRegistry> permissionsRegistryCaptor =
ArgumentCaptor.forClass(PermissionsRegistry.class);

Expand All @@ -266,6 +272,7 @@ public void onReattachedToActivityForConfigChanges_setsActivityAndPermissionsReg
plugin.cameraControlHostApiImpl = mockCameraControlHostApiImpl;
plugin.deviceOrientationManagerHostApiImpl = mockDeviceOrientationManagerHostApiImpl;
plugin.liveDataHostApiImpl = mock(LiveDataHostApiImpl.class);
plugin.camera2CameraControlHostApiImpl = mockCamera2CameraControlHostApiImpl;

plugin.onAttachedToEngine(flutterPluginBinding);
plugin.onReattachedToActivityForConfigChanges(activityPluginBinding);
Expand All @@ -282,6 +289,7 @@ public void onReattachedToActivityForConfigChanges_setsActivityAndPermissionsReg
verify(mockImageCaptureHostApiImpl).setContext(mockActivity);
verify(mockImageAnalysisHostApiImpl).setContext(mockActivity);
verify(mockCameraControlHostApiImpl).setContext(mockActivity);
verify(mockCamera2CameraControlHostApiImpl).setContext(mockActivity);

// Check permissions registry reference is set.
verify(mockSystemServicesHostApiImpl)
Expand Down Expand Up @@ -331,6 +339,8 @@ public void onDetachedFromActivity_setsContextReferencesBasedOnFlutterPluginBind
final ImageCaptureHostApiImpl mockImageCaptureHostApiImpl = mock(ImageCaptureHostApiImpl.class);
final CameraControlHostApiImpl mockCameraControlHostApiImpl =
mock(CameraControlHostApiImpl.class);
final Camera2CameraControlHostApiImpl mockCamera2CameraControlHostApiImpl =
mock(Camera2CameraControlHostApiImpl.class);
final ArgumentCaptor<PermissionsRegistry> permissionsRegistryCaptor =
ArgumentCaptor.forClass(PermissionsRegistry.class);

Expand All @@ -344,6 +354,7 @@ public void onDetachedFromActivity_setsContextReferencesBasedOnFlutterPluginBind
plugin.imageAnalysisHostApiImpl = mockImageAnalysisHostApiImpl;
plugin.cameraControlHostApiImpl = mockCameraControlHostApiImpl;
plugin.liveDataHostApiImpl = mock(LiveDataHostApiImpl.class);
plugin.camera2CameraControlHostApiImpl = mockCamera2CameraControlHostApiImpl;

plugin.onAttachedToEngine(flutterPluginBinding);
plugin.onDetachedFromActivity();
Expand All @@ -355,5 +366,6 @@ public void onDetachedFromActivity_setsContextReferencesBasedOnFlutterPluginBind
verify(mockImageCaptureHostApiImpl).setContext(mockContext);
verify(mockImageAnalysisHostApiImpl).setContext(mockContext);
verify(mockCameraControlHostApiImpl).setContext(mockContext);
verify(mockCamera2CameraControlHostApiImpl).setContext(mockContext);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -468,14 +468,14 @@ class AndroidCameraCameraX extends CameraPlatform {
/// [cameraId] is not used
@override
Future<void> setExposureMode(int cameraId, ExposureMode mode) async {
// TODO(camsim99): In a previous PR, this will be made a field, so use that instead once it lands.
// TODO(camsim99): In a previous PR, this will be made a class field, so use that instead once it lands.
final CameraControl cameraControl = await camera!.getCameraControl();
final Camera2CameraControl camera2Control =
Camera2CameraControl(cameraControl: cameraControl);
proxy.getCamera2CameraControl(cameraControl);
final bool lockExposureMode = mode == ExposureMode.locked;

final CaptureRequestOptions captureRequestOptions = CaptureRequestOptions(
requestedOptions: <(
final CaptureRequestOptions captureRequestOptions = proxy
.createCaptureRequestOptions(<(
CaptureRequestKeySupportedType,
Object?
)>[(CaptureRequestKeySupportedType.controlAeLock, lockExposureMode)]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
import 'dart:ui' show Size;

import 'analyzer.dart';
import 'camera2_camera_control.dart';
import 'camera_control.dart';
import 'camera_selector.dart';
import 'camera_state.dart';
import 'camerax_library.g.dart';
import 'capture_request_options.dart';
import 'device_orientation_manager.dart';
import 'fallback_strategy.dart';
import 'image_analysis.dart';
Expand Down Expand Up @@ -49,6 +52,8 @@ class CameraXProxy {
_startListeningForDeviceOrientationChange,
this.setPreviewSurfaceProvider = _setPreviewSurfaceProvider,
this.getDefaultDisplayRotation = _getDefaultDisplayRotation,
this.getCamera2CameraControl = _getCamera2CameraControl,
this.createCaptureRequestOptions = _createCaptureRequestOptions,
});

/// Returns a [ProcessCameraProvider] instance.
Expand Down Expand Up @@ -137,6 +142,15 @@ class CameraXProxy {
/// rotation constants.
Future<int> Function() getDefaultDisplayRotation;

/// Get [Camera2CameraControl] instance from [cameraControl].
Camera2CameraControl Function(CameraControl cameraControl)
getCamera2CameraControl;

/// Create [CapureRequestOptions] with specified options.
CaptureRequestOptions Function(
List<(CaptureRequestKeySupportedType, Object?)> options)
createCaptureRequestOptions;

static Future<ProcessCameraProvider> _getProcessCameraProvider() {
return ProcessCameraProvider.getInstance();
}
Expand Down Expand Up @@ -239,4 +253,14 @@ class CameraXProxy {
static Future<int> _getDefaultDisplayRotation() async {
return DeviceOrientationManager.getDefaultDisplayRotation();
}

static Camera2CameraControl _getCamera2CameraControl(
CameraControl cameraControl) {
return Camera2CameraControl(cameraControl: cameraControl);
}

static CaptureRequestOptions _createCaptureRequestOptions(
List<(CaptureRequestKeySupportedType, Object?)> options) {
return CaptureRequestOptions(requestedOptions: options);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ import 'package:async/async.dart';
import 'package:camera_android_camerax/camera_android_camerax.dart';
import 'package:camera_android_camerax/src/analyzer.dart';
import 'package:camera_android_camerax/src/camera.dart';
import 'package:camera_android_camerax/src/camera2_camera_control.dart';
import 'package:camera_android_camerax/src/camera_control.dart';
import 'package:camera_android_camerax/src/camera_info.dart';
import 'package:camera_android_camerax/src/camera_selector.dart';
import 'package:camera_android_camerax/src/camera_state.dart';
import 'package:camera_android_camerax/src/camera_state_error.dart';
import 'package:camera_android_camerax/src/camerax_library.g.dart';
import 'package:camera_android_camerax/src/camerax_proxy.dart';
import 'package:camera_android_camerax/src/capture_request_options.dart';
import 'package:camera_android_camerax/src/device_orientation_manager.dart';
import 'package:camera_android_camerax/src/exposure_state.dart';
import 'package:camera_android_camerax/src/fallback_strategy.dart';
Expand Down Expand Up @@ -74,6 +76,7 @@ import 'test_camerax_library.g.dart';
MockSpec<TestInstanceManagerHostApi>(),
MockSpec<TestSystemServicesHostApi>(),
MockSpec<ZoomState>(),
MockSpec<Camera2CameraControl>(),
])
@GenerateMocks(<Type>[], customMocks: <MockSpec<Object>>[
MockSpec<LiveData<CameraState>>(as: #MockLiveCameraState),
Expand Down Expand Up @@ -1981,4 +1984,59 @@ void main() {
await camera.unlockCaptureOrientation(cameraId);
expect(camera.captureOrientationLocked, isFalse);
});

test('setExposureMode sets expected controlAeLock value via Camera2 interop',
() async {
final AndroidCameraCameraX camera = AndroidCameraCameraX();
const int cameraId = 78;
final MockCameraControl mockCameraControl = MockCameraControl();
final MockCamera2CameraControl mockCamera2CameraControl =
MockCamera2CameraControl();

// Set directly for test versus calling createCamera.
camera.camera = MockCamera();

// Tell plugin to create detached Camera2CameraControl and
// CaptureRequestOptions instances for testing.
camera.proxy = CameraXProxy(
getCamera2CameraControl: (CameraControl cameraControl) =>
cameraControl == mockCameraControl
? mockCamera2CameraControl
: Camera2CameraControl.detached(cameraControl: cameraControl),
createCaptureRequestOptions:
(List<(CaptureRequestKeySupportedType, Object?)> options) =>
CaptureRequestOptions.detached(requestedOptions: options),
);

when(camera.camera!.getCameraControl())
.thenAnswer((_) async => mockCameraControl);

// Test auto mode.
await camera.setExposureMode(cameraId, ExposureMode.auto);

VerificationResult verificationResult =
verify(mockCamera2CameraControl.addCaptureRequestOptions(captureAny));
CaptureRequestOptions capturedCaptureRequestOptions =
verificationResult.captured.single as CaptureRequestOptions;
List<(CaptureRequestKeySupportedType, Object?)> requestedOptions =
capturedCaptureRequestOptions.requestedOptions;
expect(requestedOptions.length, equals(1));
expect(requestedOptions.first.$1,
equals(CaptureRequestKeySupportedType.controlAeLock));
expect(requestedOptions.first.$2, equals(false));

// Test locked mode.
clearInteractions(mockCamera2CameraControl);
await camera.setExposureMode(cameraId, ExposureMode.locked);

verificationResult =
verify(mockCamera2CameraControl.addCaptureRequestOptions(captureAny));
capturedCaptureRequestOptions =
verificationResult.captured.single as CaptureRequestOptions;
requestedOptions = capturedCaptureRequestOptions.requestedOptions;
expect(requestedOptions.length, equals(1));
expect(requestedOptions.first.$1,
equals(CaptureRequestKeySupportedType.controlAeLock));
expect(requestedOptions.first.$2, equals(true));
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@ import 'dart:typed_data' as _i29;

import 'package:camera_android_camerax/src/analyzer.dart' as _i15;
import 'package:camera_android_camerax/src/camera.dart' as _i9;
import 'package:camera_android_camerax/src/camera2_camera_control.dart' as _i38;
import 'package:camera_android_camerax/src/camera_control.dart' as _i3;
import 'package:camera_android_camerax/src/camera_info.dart' as _i2;
import 'package:camera_android_camerax/src/camera_selector.dart' as _i22;
import 'package:camera_android_camerax/src/camera_state.dart' as _i18;
import 'package:camera_android_camerax/src/camerax_library.g.dart' as _i7;
import 'package:camera_android_camerax/src/capture_request_options.dart'
as _i39;
import 'package:camera_android_camerax/src/exposure_state.dart' as _i5;
import 'package:camera_android_camerax/src/fallback_strategy.dart' as _i23;
import 'package:camera_android_camerax/src/focus_metering_action.dart' as _i21;
Expand Down Expand Up @@ -1317,6 +1320,38 @@ class MockZoomState extends _i1.Mock implements _i19.ZoomState {
) as double);
}

/// A class which mocks [Camera2CameraControl].
///
/// See the documentation for Mockito's code generation for more information.
// ignore: must_be_immutable
class MockCamera2CameraControl extends _i1.Mock
implements _i38.Camera2CameraControl {
@override
_i3.CameraControl get cameraControl => (super.noSuchMethod(
Invocation.getter(#cameraControl),
returnValue: _FakeCameraControl_1(
this,
Invocation.getter(#cameraControl),
),
returnValueForMissingStub: _FakeCameraControl_1(
this,
Invocation.getter(#cameraControl),
),
) as _i3.CameraControl);

@override
_i16.Future<void> addCaptureRequestOptions(
_i39.CaptureRequestOptions? captureRequestOptions) =>
(super.noSuchMethod(
Invocation.method(
#addCaptureRequestOptions,
[captureRequestOptions],
),
returnValue: _i16.Future<void>.value(),
returnValueForMissingStub: _i16.Future<void>.value(),
) as _i16.Future<void>);
}

/// A class which mocks [LiveData].
///
/// See the documentation for Mockito's code generation for more information.
Expand Down
Loading