blob: 17b06cddc1ae3012d7ab8b3fcc956812e3a656af [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.
import 'dart:async';
import 'dart:math';
import 'package:async/async.dart';
import 'package:camera_avfoundation/src/avfoundation_camera.dart';
import 'package:camera_avfoundation/src/utils.dart';
import 'package:camera_platform_interface/camera_platform_interface.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'method_channel_mock.dart';
const String _channelName = 'plugins.flutter.io/camera_avfoundation';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
test('registers instance', () async {
AVFoundationCamera.registerWith();
expect(CameraPlatform.instance, isA<AVFoundationCamera>());
});
test('registration does not set message handlers', () async {
AVFoundationCamera.registerWith();
// Setting up a handler requires bindings to be initialized, and since
// registerWith is called very early in initialization the bindings won't
// have been initialized. While registerWith could intialize them, that
// could slow down startup, so instead the handler should be set up lazily.
final ByteData? response =
await _ambiguate(TestDefaultBinaryMessengerBinding.instance)!
.defaultBinaryMessenger
.handlePlatformMessage(
AVFoundationCamera.deviceEventChannelName,
const StandardMethodCodec().encodeMethodCall(const MethodCall(
'orientation_changed',
<String, Object>{'orientation': 'portraitDown'})),
(ByteData? data) {});
expect(response, null);
});
group('Creation, Initialization & Disposal Tests', () {
test('Should send creation data and receive back a camera id', () async {
// Arrange
final MethodChannelMock cameraMockChannel = MethodChannelMock(
channelName: _channelName,
methods: <String, dynamic>{
'create': <String, dynamic>{
'cameraId': 1,
'imageFormatGroup': 'unknown',
}
});
final AVFoundationCamera camera = AVFoundationCamera();
// Act
final int cameraId = await camera.createCamera(
const CameraDescription(
name: 'Test',
lensDirection: CameraLensDirection.back,
sensorOrientation: 0),
ResolutionPreset.high,
);
// Assert
expect(cameraMockChannel.log, <Matcher>[
isMethodCall(
'create',
arguments: <String, Object?>{
'cameraName': 'Test',
'resolutionPreset': 'high',
'enableAudio': false
},
),
]);
expect(cameraId, 1);
});
test('Should throw CameraException when create throws a PlatformException',
() {
// Arrange
MethodChannelMock(channelName: _channelName, methods: <String, dynamic>{
'create': PlatformException(
code: 'TESTING_ERROR_CODE',
message: 'Mock error message used during testing.',
)
});
final AVFoundationCamera camera = AVFoundationCamera();
// Act
expect(
() => camera.createCamera(
const CameraDescription(
name: 'Test',
lensDirection: CameraLensDirection.back,
sensorOrientation: 0,
),
ResolutionPreset.high,
),
throwsA(
isA<CameraException>()
.having(
(CameraException e) => e.code, 'code', 'TESTING_ERROR_CODE')
.having((CameraException e) => e.description, 'description',
'Mock error message used during testing.'),
),
);
});
test('Should throw CameraException when create throws a PlatformException',
() {
// Arrange
MethodChannelMock(channelName: _channelName, methods: <String, dynamic>{
'create': PlatformException(
code: 'TESTING_ERROR_CODE',
message: 'Mock error message used during testing.',
)
});
final AVFoundationCamera camera = AVFoundationCamera();
// Act
expect(
() => camera.createCamera(
const CameraDescription(
name: 'Test',
lensDirection: CameraLensDirection.back,
sensorOrientation: 0,
),
ResolutionPreset.high,
),
throwsA(
isA<CameraException>()
.having(
(CameraException e) => e.code, 'code', 'TESTING_ERROR_CODE')
.having((CameraException e) => e.description, 'description',
'Mock error message used during testing.'),
),
);
});
test(
'Should throw CameraException when initialize throws a PlatformException',
() {
// Arrange
MethodChannelMock(
channelName: _channelName,
methods: <String, dynamic>{
'initialize': PlatformException(
code: 'TESTING_ERROR_CODE',
message: 'Mock error message used during testing.',
)
},
);
final AVFoundationCamera camera = AVFoundationCamera();
// Act
expect(
() => camera.initializeCamera(0),
throwsA(
isA<CameraException>()
.having(
(CameraException e) => e.code, 'code', 'TESTING_ERROR_CODE')
.having(
(CameraException e) => e.description,
'description',
'Mock error message used during testing.',
),
),
);
},
);
test('Should send initialization data', () async {
// Arrange
final MethodChannelMock cameraMockChannel = MethodChannelMock(
channelName: _channelName,
methods: <String, dynamic>{
'create': <String, dynamic>{
'cameraId': 1,
'imageFormatGroup': 'unknown',
},
'initialize': null
});
final AVFoundationCamera camera = AVFoundationCamera();
final int cameraId = await camera.createCamera(
const CameraDescription(
name: 'Test',
lensDirection: CameraLensDirection.back,
sensorOrientation: 0,
),
ResolutionPreset.high,
);
// Act
final Future<void> initializeFuture = camera.initializeCamera(cameraId);
camera.cameraEventStreamController.add(CameraInitializedEvent(
cameraId,
1920,
1080,
ExposureMode.auto,
true,
FocusMode.auto,
true,
));
await initializeFuture;
// Assert
expect(cameraId, 1);
expect(cameraMockChannel.log, <Matcher>[
anything,
isMethodCall(
'initialize',
arguments: <String, Object?>{
'cameraId': 1,
'imageFormatGroup': 'unknown',
},
),
]);
});
test('Should send a disposal call on dispose', () async {
// Arrange
final MethodChannelMock cameraMockChannel = MethodChannelMock(
channelName: _channelName,
methods: <String, dynamic>{
'create': <String, dynamic>{'cameraId': 1},
'initialize': null,
'dispose': <String, dynamic>{'cameraId': 1}
});
final AVFoundationCamera camera = AVFoundationCamera();
final int cameraId = await camera.createCamera(
const CameraDescription(
name: 'Test',
lensDirection: CameraLensDirection.back,
sensorOrientation: 0,
),
ResolutionPreset.high,
);
final Future<void> initializeFuture = camera.initializeCamera(cameraId);
camera.cameraEventStreamController.add(CameraInitializedEvent(
cameraId,
1920,
1080,
ExposureMode.auto,
true,
FocusMode.auto,
true,
));
await initializeFuture;
// Act
await camera.dispose(cameraId);
// Assert
expect(cameraId, 1);
expect(cameraMockChannel.log, <Matcher>[
anything,
anything,
isMethodCall(
'dispose',
arguments: <String, Object?>{'cameraId': 1},
),
]);
});
});
group('Event Tests', () {
late AVFoundationCamera camera;
late int cameraId;
setUp(() async {
MethodChannelMock(
channelName: _channelName,
methods: <String, dynamic>{
'create': <String, dynamic>{'cameraId': 1},
'initialize': null
},
);
camera = AVFoundationCamera();
cameraId = await camera.createCamera(
const CameraDescription(
name: 'Test',
lensDirection: CameraLensDirection.back,
sensorOrientation: 0,
),
ResolutionPreset.high,
);
final Future<void> initializeFuture = camera.initializeCamera(cameraId);
camera.cameraEventStreamController.add(CameraInitializedEvent(
cameraId,
1920,
1080,
ExposureMode.auto,
true,
FocusMode.auto,
true,
));
await initializeFuture;
});
test('Should receive initialized event', () async {
// Act
final Stream<CameraInitializedEvent> eventStream =
camera.onCameraInitialized(cameraId);
final StreamQueue<CameraInitializedEvent> streamQueue =
StreamQueue<CameraInitializedEvent>(eventStream);
// Emit test events
final CameraInitializedEvent event = CameraInitializedEvent(
cameraId,
3840,
2160,
ExposureMode.auto,
true,
FocusMode.auto,
true,
);
await camera.handleCameraMethodCall(
MethodCall('initialized', event.toJson()), cameraId);
// Assert
expect(await streamQueue.next, event);
// Clean up
await streamQueue.cancel();
});
test('Should receive resolution changes', () async {
// Act
final Stream<CameraResolutionChangedEvent> resolutionStream =
camera.onCameraResolutionChanged(cameraId);
final StreamQueue<CameraResolutionChangedEvent> streamQueue =
StreamQueue<CameraResolutionChangedEvent>(resolutionStream);
// Emit test events
final CameraResolutionChangedEvent fhdEvent =
CameraResolutionChangedEvent(cameraId, 1920, 1080);
final CameraResolutionChangedEvent uhdEvent =
CameraResolutionChangedEvent(cameraId, 3840, 2160);
await camera.handleCameraMethodCall(
MethodCall('resolution_changed', fhdEvent.toJson()), cameraId);
await camera.handleCameraMethodCall(
MethodCall('resolution_changed', uhdEvent.toJson()), cameraId);
await camera.handleCameraMethodCall(
MethodCall('resolution_changed', fhdEvent.toJson()), cameraId);
await camera.handleCameraMethodCall(
MethodCall('resolution_changed', uhdEvent.toJson()), cameraId);
// Assert
expect(await streamQueue.next, fhdEvent);
expect(await streamQueue.next, uhdEvent);
expect(await streamQueue.next, fhdEvent);
expect(await streamQueue.next, uhdEvent);
// Clean up
await streamQueue.cancel();
});
test('Should receive camera closing events', () async {
// Act
final Stream<CameraClosingEvent> eventStream =
camera.onCameraClosing(cameraId);
final StreamQueue<CameraClosingEvent> streamQueue =
StreamQueue<CameraClosingEvent>(eventStream);
// Emit test events
final CameraClosingEvent event = CameraClosingEvent(cameraId);
await camera.handleCameraMethodCall(
MethodCall('camera_closing', event.toJson()), cameraId);
await camera.handleCameraMethodCall(
MethodCall('camera_closing', event.toJson()), cameraId);
await camera.handleCameraMethodCall(
MethodCall('camera_closing', event.toJson()), cameraId);
// Assert
expect(await streamQueue.next, event);
expect(await streamQueue.next, event);
expect(await streamQueue.next, event);
// Clean up
await streamQueue.cancel();
});
test('Should receive camera error events', () async {
// Act
final Stream<CameraErrorEvent> errorStream =
camera.onCameraError(cameraId);
final StreamQueue<CameraErrorEvent> streamQueue =
StreamQueue<CameraErrorEvent>(errorStream);
// Emit test events
final CameraErrorEvent event =
CameraErrorEvent(cameraId, 'Error Description');
await camera.handleCameraMethodCall(
MethodCall('error', event.toJson()), cameraId);
await camera.handleCameraMethodCall(
MethodCall('error', event.toJson()), cameraId);
await camera.handleCameraMethodCall(
MethodCall('error', event.toJson()), cameraId);
// Assert
expect(await streamQueue.next, event);
expect(await streamQueue.next, event);
expect(await streamQueue.next, event);
// Clean up
await streamQueue.cancel();
});
test('Should receive device orientation change events', () async {
// Act
final Stream<DeviceOrientationChangedEvent> eventStream =
camera.onDeviceOrientationChanged();
final StreamQueue<DeviceOrientationChangedEvent> streamQueue =
StreamQueue<DeviceOrientationChangedEvent>(eventStream);
// Emit test events
const DeviceOrientationChangedEvent event =
DeviceOrientationChangedEvent(DeviceOrientation.portraitUp);
for (int i = 0; i < 3; i++) {
await _ambiguate(TestDefaultBinaryMessengerBinding.instance)!
.defaultBinaryMessenger
.handlePlatformMessage(
AVFoundationCamera.deviceEventChannelName,
const StandardMethodCodec().encodeMethodCall(
MethodCall('orientation_changed', event.toJson())),
null);
}
// Assert
expect(await streamQueue.next, event);
expect(await streamQueue.next, event);
expect(await streamQueue.next, event);
// Clean up
await streamQueue.cancel();
});
});
group('Function Tests', () {
late AVFoundationCamera camera;
late int cameraId;
setUp(() async {
MethodChannelMock(
channelName: _channelName,
methods: <String, dynamic>{
'create': <String, dynamic>{'cameraId': 1},
'initialize': null
},
);
camera = AVFoundationCamera();
cameraId = await camera.createCamera(
const CameraDescription(
name: 'Test',
lensDirection: CameraLensDirection.back,
sensorOrientation: 0,
),
ResolutionPreset.high,
);
final Future<void> initializeFuture = camera.initializeCamera(cameraId);
camera.cameraEventStreamController.add(
CameraInitializedEvent(
cameraId,
1920,
1080,
ExposureMode.auto,
true,
FocusMode.auto,
true,
),
);
await initializeFuture;
});
test('Should fetch CameraDescription instances for available cameras',
() async {
// Arrange
// This deliberately uses 'dynamic' since that's what actual platform
// channel results will be, so using typed mock data could mask type
// handling bugs in the code under test.
final List<dynamic> returnData = <dynamic>[
<String, dynamic>{
'name': 'Test 1',
'lensFacing': 'front',
'sensorOrientation': 1
},
<String, dynamic>{
'name': 'Test 2',
'lensFacing': 'back',
'sensorOrientation': 2
}
];
final MethodChannelMock channel = MethodChannelMock(
channelName: _channelName,
methods: <String, dynamic>{'availableCameras': returnData},
);
// Act
final List<CameraDescription> cameras = await camera.availableCameras();
// Assert
expect(channel.log, <Matcher>[
isMethodCall('availableCameras', arguments: null),
]);
expect(cameras.length, returnData.length);
for (int i = 0; i < returnData.length; i++) {
final Map<String, Object?> typedData =
(returnData[i] as Map<dynamic, dynamic>).cast<String, Object?>();
final CameraDescription cameraDescription = CameraDescription(
name: typedData['name']! as String,
lensDirection:
parseCameraLensDirection(typedData['lensFacing']! as String),
sensorOrientation: typedData['sensorOrientation']! as int,
);
expect(cameras[i], cameraDescription);
}
});
test(
'Should throw CameraException when availableCameras throws a PlatformException',
() {
// Arrange
MethodChannelMock(channelName: _channelName, methods: <String, dynamic>{
'availableCameras': PlatformException(
code: 'TESTING_ERROR_CODE',
message: 'Mock error message used during testing.',
)
});
// Act
expect(
camera.availableCameras,
throwsA(
isA<CameraException>()
.having(
(CameraException e) => e.code, 'code', 'TESTING_ERROR_CODE')
.having((CameraException e) => e.description, 'description',
'Mock error message used during testing.'),
),
);
});
test('Should take a picture and return an XFile instance', () async {
// Arrange
final MethodChannelMock channel = MethodChannelMock(
channelName: _channelName,
methods: <String, dynamic>{'takePicture': '/test/path.jpg'});
// Act
final XFile file = await camera.takePicture(cameraId);
// Assert
expect(channel.log, <Matcher>[
isMethodCall('takePicture', arguments: <String, Object?>{
'cameraId': cameraId,
}),
]);
expect(file.path, '/test/path.jpg');
});
test('Should prepare for video recording', () async {
// Arrange
final MethodChannelMock channel = MethodChannelMock(
channelName: _channelName,
methods: <String, dynamic>{'prepareForVideoRecording': null},
);
// Act
await camera.prepareForVideoRecording();
// Assert
expect(channel.log, <Matcher>[
isMethodCall('prepareForVideoRecording', arguments: null),
]);
});
test('Should start recording a video', () async {
// Arrange
final MethodChannelMock channel = MethodChannelMock(
channelName: _channelName,
methods: <String, dynamic>{'startVideoRecording': null},
);
// Act
await camera.startVideoRecording(cameraId);
// Assert
expect(channel.log, <Matcher>[
isMethodCall('startVideoRecording', arguments: <String, Object?>{
'cameraId': cameraId,
'maxVideoDuration': null,
'enableStream': false,
}),
]);
});
test('Should pass maxVideoDuration when starting recording a video',
() async {
// Arrange
final MethodChannelMock channel = MethodChannelMock(
channelName: _channelName,
methods: <String, dynamic>{'startVideoRecording': null},
);
// Act
await camera.startVideoRecording(
cameraId,
maxVideoDuration: const Duration(seconds: 10),
);
// Assert
expect(channel.log, <Matcher>[
isMethodCall('startVideoRecording', arguments: <String, Object?>{
'cameraId': cameraId,
'maxVideoDuration': 10000,
'enableStream': false,
}),
]);
});
test(
'Should pass enableStream if callback is passed when starting recording a video',
() async {
// Arrange
final MethodChannelMock channel = MethodChannelMock(
channelName: _channelName,
methods: <String, dynamic>{'startVideoRecording': null},
);
// Act
await camera.startVideoCapturing(VideoCaptureOptions(cameraId,
streamCallback: (CameraImageData imageData) {}));
// Assert
expect(channel.log, <Matcher>[
isMethodCall('startVideoRecording', arguments: <String, Object?>{
'cameraId': cameraId,
'maxVideoDuration': null,
'enableStream': true,
}),
]);
});
test('Should stop a video recording and return the file', () async {
// Arrange
final MethodChannelMock channel = MethodChannelMock(
channelName: _channelName,
methods: <String, dynamic>{'stopVideoRecording': '/test/path.mp4'},
);
// Act
final XFile file = await camera.stopVideoRecording(cameraId);
// Assert
expect(channel.log, <Matcher>[
isMethodCall('stopVideoRecording', arguments: <String, Object?>{
'cameraId': cameraId,
}),
]);
expect(file.path, '/test/path.mp4');
});
test('Should pause a video recording', () async {
// Arrange
final MethodChannelMock channel = MethodChannelMock(
channelName: _channelName,
methods: <String, dynamic>{'pauseVideoRecording': null},
);
// Act
await camera.pauseVideoRecording(cameraId);
// Assert
expect(channel.log, <Matcher>[
isMethodCall('pauseVideoRecording', arguments: <String, Object?>{
'cameraId': cameraId,
}),
]);
});
test('Should resume a video recording', () async {
// Arrange
final MethodChannelMock channel = MethodChannelMock(
channelName: _channelName,
methods: <String, dynamic>{'resumeVideoRecording': null},
);
// Act
await camera.resumeVideoRecording(cameraId);
// Assert
expect(channel.log, <Matcher>[
isMethodCall('resumeVideoRecording', arguments: <String, Object?>{
'cameraId': cameraId,
}),
]);
});
test('Should set the description while recording', () async {
// Arrange
final MethodChannelMock channel = MethodChannelMock(
channelName: _channelName,
methods: <String, dynamic>{'setDescriptionWhileRecording': null},
);
const CameraDescription camera2Description = CameraDescription(
name: 'Test2',
lensDirection: CameraLensDirection.front,
sensorOrientation: 0);
// Act
await camera.setDescriptionWhileRecording(camera2Description);
// Assert
expect(channel.log, <Matcher>[
isMethodCall('setDescriptionWhileRecording',
arguments: <String, Object?>{
'cameraName': camera2Description.name,
}),
]);
});
test('Should set the flash mode', () async {
// Arrange
final MethodChannelMock channel = MethodChannelMock(
channelName: _channelName,
methods: <String, dynamic>{'setFlashMode': null},
);
// Act
await camera.setFlashMode(cameraId, FlashMode.torch);
await camera.setFlashMode(cameraId, FlashMode.always);
await camera.setFlashMode(cameraId, FlashMode.auto);
await camera.setFlashMode(cameraId, FlashMode.off);
// Assert
expect(channel.log, <Matcher>[
isMethodCall('setFlashMode', arguments: <String, Object?>{
'cameraId': cameraId,
'mode': 'torch'
}),
isMethodCall('setFlashMode', arguments: <String, Object?>{
'cameraId': cameraId,
'mode': 'always'
}),
isMethodCall('setFlashMode',
arguments: <String, Object?>{'cameraId': cameraId, 'mode': 'auto'}),
isMethodCall('setFlashMode',
arguments: <String, Object?>{'cameraId': cameraId, 'mode': 'off'}),
]);
});
test('Should set the exposure mode', () async {
// Arrange
final MethodChannelMock channel = MethodChannelMock(
channelName: _channelName,
methods: <String, dynamic>{'setExposureMode': null},
);
// Act
await camera.setExposureMode(cameraId, ExposureMode.auto);
await camera.setExposureMode(cameraId, ExposureMode.locked);
// Assert
expect(channel.log, <Matcher>[
isMethodCall('setExposureMode',
arguments: <String, Object?>{'cameraId': cameraId, 'mode': 'auto'}),
isMethodCall('setExposureMode', arguments: <String, Object?>{
'cameraId': cameraId,
'mode': 'locked'
}),
]);
});
test('Should set the exposure point', () async {
// Arrange
final MethodChannelMock channel = MethodChannelMock(
channelName: _channelName,
methods: <String, dynamic>{'setExposurePoint': null},
);
// Act
await camera.setExposurePoint(cameraId, const Point<double>(0.5, 0.5));
await camera.setExposurePoint(cameraId, null);
// Assert
expect(channel.log, <Matcher>[
isMethodCall('setExposurePoint', arguments: <String, Object?>{
'cameraId': cameraId,
'x': 0.5,
'y': 0.5,
'reset': false
}),
isMethodCall('setExposurePoint', arguments: <String, Object?>{
'cameraId': cameraId,
'x': null,
'y': null,
'reset': true
}),
]);
});
test('Should get the min exposure offset', () async {
// Arrange
final MethodChannelMock channel = MethodChannelMock(
channelName: _channelName,
methods: <String, dynamic>{'getMinExposureOffset': 2.0},
);
// Act
final double minExposureOffset =
await camera.getMinExposureOffset(cameraId);
// Assert
expect(minExposureOffset, 2.0);
expect(channel.log, <Matcher>[
isMethodCall('getMinExposureOffset', arguments: <String, Object?>{
'cameraId': cameraId,
}),
]);
});
test('Should get the max exposure offset', () async {
// Arrange
final MethodChannelMock channel = MethodChannelMock(
channelName: _channelName,
methods: <String, dynamic>{'getMaxExposureOffset': 2.0},
);
// Act
final double maxExposureOffset =
await camera.getMaxExposureOffset(cameraId);
// Assert
expect(maxExposureOffset, 2.0);
expect(channel.log, <Matcher>[
isMethodCall('getMaxExposureOffset', arguments: <String, Object?>{
'cameraId': cameraId,
}),
]);
});
test('Should get the exposure offset step size', () async {
// Arrange
final MethodChannelMock channel = MethodChannelMock(
channelName: _channelName,
methods: <String, dynamic>{'getExposureOffsetStepSize': 0.25},
);
// Act
final double stepSize = await camera.getExposureOffsetStepSize(cameraId);
// Assert
expect(stepSize, 0.25);
expect(channel.log, <Matcher>[
isMethodCall('getExposureOffsetStepSize', arguments: <String, Object?>{
'cameraId': cameraId,
}),
]);
});
test('Should set the exposure offset', () async {
// Arrange
final MethodChannelMock channel = MethodChannelMock(
channelName: _channelName,
methods: <String, dynamic>{'setExposureOffset': 0.6},
);
// Act
final double actualOffset = await camera.setExposureOffset(cameraId, 0.5);
// Assert
expect(actualOffset, 0.6);
expect(channel.log, <Matcher>[
isMethodCall('setExposureOffset', arguments: <String, Object?>{
'cameraId': cameraId,
'offset': 0.5,
}),
]);
});
test('Should set the focus mode', () async {
// Arrange
final MethodChannelMock channel = MethodChannelMock(
channelName: _channelName,
methods: <String, dynamic>{'setFocusMode': null},
);
// Act
await camera.setFocusMode(cameraId, FocusMode.auto);
await camera.setFocusMode(cameraId, FocusMode.locked);
// Assert
expect(channel.log, <Matcher>[
isMethodCall('setFocusMode',
arguments: <String, Object?>{'cameraId': cameraId, 'mode': 'auto'}),
isMethodCall('setFocusMode', arguments: <String, Object?>{
'cameraId': cameraId,
'mode': 'locked'
}),
]);
});
test('Should set the exposure point', () async {
// Arrange
final MethodChannelMock channel = MethodChannelMock(
channelName: _channelName,
methods: <String, dynamic>{'setFocusPoint': null},
);
// Act
await camera.setFocusPoint(cameraId, const Point<double>(0.5, 0.5));
await camera.setFocusPoint(cameraId, null);
// Assert
expect(channel.log, <Matcher>[
isMethodCall('setFocusPoint', arguments: <String, Object?>{
'cameraId': cameraId,
'x': 0.5,
'y': 0.5,
'reset': false
}),
isMethodCall('setFocusPoint', arguments: <String, Object?>{
'cameraId': cameraId,
'x': null,
'y': null,
'reset': true
}),
]);
});
test('Should build a texture widget as preview widget', () async {
// Act
final Widget widget = camera.buildPreview(cameraId);
// Act
expect(widget is Texture, isTrue);
expect((widget as Texture).textureId, cameraId);
});
test('Should throw MissingPluginException when handling unknown method',
() {
final AVFoundationCamera camera = AVFoundationCamera();
expect(
() => camera.handleCameraMethodCall(
const MethodCall('unknown_method'), 1),
throwsA(isA<MissingPluginException>()));
});
test('Should get the max zoom level', () async {
// Arrange
final MethodChannelMock channel = MethodChannelMock(
channelName: _channelName,
methods: <String, dynamic>{'getMaxZoomLevel': 10.0},
);
// Act
final double maxZoomLevel = await camera.getMaxZoomLevel(cameraId);
// Assert
expect(maxZoomLevel, 10.0);
expect(channel.log, <Matcher>[
isMethodCall('getMaxZoomLevel', arguments: <String, Object?>{
'cameraId': cameraId,
}),
]);
});
test('Should get the min zoom level', () async {
// Arrange
final MethodChannelMock channel = MethodChannelMock(
channelName: _channelName,
methods: <String, dynamic>{'getMinZoomLevel': 1.0},
);
// Act
final double maxZoomLevel = await camera.getMinZoomLevel(cameraId);
// Assert
expect(maxZoomLevel, 1.0);
expect(channel.log, <Matcher>[
isMethodCall('getMinZoomLevel', arguments: <String, Object?>{
'cameraId': cameraId,
}),
]);
});
test('Should set the zoom level', () async {
// Arrange
final MethodChannelMock channel = MethodChannelMock(
channelName: _channelName,
methods: <String, dynamic>{'setZoomLevel': null},
);
// Act
await camera.setZoomLevel(cameraId, 2.0);
// Assert
expect(channel.log, <Matcher>[
isMethodCall('setZoomLevel',
arguments: <String, Object?>{'cameraId': cameraId, 'zoom': 2.0}),
]);
});
test('Should throw CameraException when illegal zoom level is supplied',
() async {
// Arrange
MethodChannelMock(
channelName: _channelName,
methods: <String, dynamic>{
'setZoomLevel': PlatformException(
code: 'ZOOM_ERROR',
message: 'Illegal zoom error',
)
},
);
// Act & assert
expect(
() => camera.setZoomLevel(cameraId, -1.0),
throwsA(isA<CameraException>()
.having((CameraException e) => e.code, 'code', 'ZOOM_ERROR')
.having((CameraException e) => e.description, 'description',
'Illegal zoom error')));
});
test('Should lock the capture orientation', () async {
// Arrange
final MethodChannelMock channel = MethodChannelMock(
channelName: _channelName,
methods: <String, dynamic>{'lockCaptureOrientation': null},
);
// Act
await camera.lockCaptureOrientation(
cameraId, DeviceOrientation.portraitUp);
// Assert
expect(channel.log, <Matcher>[
isMethodCall('lockCaptureOrientation', arguments: <String, Object?>{
'cameraId': cameraId,
'orientation': 'portraitUp'
}),
]);
});
test('Should unlock the capture orientation', () async {
// Arrange
final MethodChannelMock channel = MethodChannelMock(
channelName: _channelName,
methods: <String, dynamic>{'unlockCaptureOrientation': null},
);
// Act
await camera.unlockCaptureOrientation(cameraId);
// Assert
expect(channel.log, <Matcher>[
isMethodCall('unlockCaptureOrientation',
arguments: <String, Object?>{'cameraId': cameraId}),
]);
});
test('Should pause the camera preview', () async {
// Arrange
final MethodChannelMock channel = MethodChannelMock(
channelName: _channelName,
methods: <String, dynamic>{'pausePreview': null},
);
// Act
await camera.pausePreview(cameraId);
// Assert
expect(channel.log, <Matcher>[
isMethodCall('pausePreview',
arguments: <String, Object?>{'cameraId': cameraId}),
]);
});
test('Should resume the camera preview', () async {
// Arrange
final MethodChannelMock channel = MethodChannelMock(
channelName: _channelName,
methods: <String, dynamic>{'resumePreview': null},
);
// Act
await camera.resumePreview(cameraId);
// Assert
expect(channel.log, <Matcher>[
isMethodCall('resumePreview',
arguments: <String, Object?>{'cameraId': cameraId}),
]);
});
test('Should start streaming', () async {
// Arrange
final MethodChannelMock channel = MethodChannelMock(
channelName: _channelName,
methods: <String, dynamic>{
'startImageStream': null,
'stopImageStream': null,
},
);
// Act
final StreamSubscription<CameraImageData> subscription = camera
.onStreamedFrameAvailable(cameraId)
.listen((CameraImageData imageData) {});
// Assert
expect(channel.log, <Matcher>[
isMethodCall('startImageStream', arguments: null),
]);
await subscription.cancel();
});
test('Should stop streaming', () async {
// Arrange
final MethodChannelMock channel = MethodChannelMock(
channelName: _channelName,
methods: <String, dynamic>{
'startImageStream': null,
'stopImageStream': null,
},
);
// Act
final StreamSubscription<CameraImageData> subscription = camera
.onStreamedFrameAvailable(cameraId)
.listen((CameraImageData imageData) {});
await subscription.cancel();
// Assert
expect(channel.log, <Matcher>[
isMethodCall('startImageStream', arguments: null),
isMethodCall('stopImageStream', arguments: null),
]);
});
});
}
/// This allows a value of type T or T? to be treated as a value of type T?.
///
/// We use this so that APIs that have become non-nullable can still be used
/// with `!` and `?` on the stable branch.
T? _ambiguate<T>(T? value) => value;