Skip to content

Commit abba683

Browse files
[camera_avfoundation] Tests backfilling - part 1 (#8698)
Backfills tests for the `CameraPlugin` class as part of flutter/flutter#119109 There are 3 new test files: - CameraPluginCreateCameraTests contains tests for the `createCamera(withName)` method - CameraPluginInitializeCameraTests contains tests for the `initializeCamera` method - ~CameraPluginForwardingMethodTests contains tests for all methods that only forward to the `FLTCam` instance without any (or almost any for `startVideoRecording`) logic~ extracted to separate PR I've also added `minimumExposureOffset` and `maximumExposureOffset` to `FLTCam` to make both easier to mock and more consistent. Before the `CameraPlugin` class used the `CaptureDevice` methods directly by accessing the `captureDevice` field of the `FLTCam` class.
1 parent f246137 commit abba683

16 files changed

+428
-64
lines changed

packages/camera/camera_avfoundation/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 0.9.18+9
2+
3+
* Backfills unit tests for `CameraPlugin` class.
4+
* Adds `minimumExposureOffset` and `maximumExposureOffset` methods to `FLTCam` class.
5+
16
## 0.9.18+8
27

38
* Migrates unit tests to Swift.

packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@
4949
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
5050
97DB234D2D566D0700CEFE66 /* CameraPreviewPauseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97DB234C2D566D0700CEFE66 /* CameraPreviewPauseTests.swift */; };
5151
E0CDBAC227CD9729002561D9 /* CameraTestUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = E0CDBAC127CD9729002561D9 /* CameraTestUtils.m */; };
52+
E12C4FF82D68E85500515E70 /* MockFLTCameraPermissionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12C4FF72D68E85500515E70 /* MockFLTCameraPermissionManager.swift */; };
53+
E1FFEAAD2D6C8DD700B14107 /* MockFLTCam.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1FFEAAC2D6C8DD700B14107 /* MockFLTCam.swift */; };
54+
E1FFEAAF2D6CDA8C00B14107 /* CameraPluginCreateCameraTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1FFEAAE2D6CDA8C00B14107 /* CameraPluginCreateCameraTests.swift */; };
55+
E1FFEAB12D6CDE5B00B14107 /* CameraPluginInitializeCameraTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1FFEAB02D6CDE5B00B14107 /* CameraPluginInitializeCameraTests.swift */; };
5256
/* End PBXBuildFile section */
5357

5458
/* Begin PBXContainerItemProxy section */
@@ -141,6 +145,10 @@
141145
B61D98BBC8FB276D1C4A7BB2 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
142146
E0CDBAC027CD9729002561D9 /* CameraTestUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CameraTestUtils.h; sourceTree = "<group>"; };
143147
E0CDBAC127CD9729002561D9 /* CameraTestUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraTestUtils.m; sourceTree = "<group>"; };
148+
E12C4FF72D68E85500515E70 /* MockFLTCameraPermissionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockFLTCameraPermissionManager.swift; sourceTree = "<group>"; };
149+
E1FFEAAC2D6C8DD700B14107 /* MockFLTCam.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockFLTCam.swift; sourceTree = "<group>"; };
150+
E1FFEAAE2D6CDA8C00B14107 /* CameraPluginCreateCameraTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraPluginCreateCameraTests.swift; sourceTree = "<group>"; };
151+
E1FFEAB02D6CDE5B00B14107 /* CameraPluginInitializeCameraTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraPluginInitializeCameraTests.swift; sourceTree = "<group>"; };
144152
E67C6DBF6478BE708993169F /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
145153
ECAF63F924EFA2D68883BA85 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; };
146154
/* End PBXFileReference section */
@@ -193,6 +201,8 @@
193201
978D90B32D5F630300CD817E /* StreamingTests.swift */,
194202
97922B0C2D6380C300A9B4CF /* SampleBufferTests.swift */,
195203
978296CE2D5F744B0009BDD3 /* PhotoCaptureTests.swift */,
204+
E1FFEAAE2D6CDA8C00B14107 /* CameraPluginCreateCameraTests.swift */,
205+
E1FFEAB02D6CDE5B00B14107 /* CameraPluginInitializeCameraTests.swift */,
196206
);
197207
path = RunnerTests;
198208
sourceTree = "<group>";
@@ -215,8 +225,8 @@
215225
7F8FD22E2D4D0B88001AF2C1 /* MockFlutterBinaryMessenger.m */,
216226
7F8FD22A2D4D07A6001AF2C1 /* MockFlutterTextureRegistry.h */,
217227
7F8FD22B2D4D07DD001AF2C1 /* MockFlutterTextureRegistry.m */,
218-
7F8FD2282D4BFABF001AF2C1 /* MockGlobalEventApi.m */,
219228
7F8FD2272D4BFA8D001AF2C1 /* MockGlobalEventApi.h */,
229+
7F8FD2282D4BFABF001AF2C1 /* MockGlobalEventApi.m */,
220230
7FD83D292D5BA49100F4DB7C /* MockCaptureConnection.h */,
221231
7FD83D2A2D5BA65B00F4DB7C /* MockCaptureConnection.m */,
222232
7FCEDD312D43C2B900EA1CA8 /* MockCaptureDevice.h */,
@@ -227,12 +237,14 @@
227237
7FD5821F2D579ECC003B1200 /* MockCapturePhotoOutput.m */,
228238
7FCEDD332D43C2B900EA1CA8 /* MockDeviceOrientationProvider.h */,
229239
7FCEDD342D43C2B900EA1CA8 /* MockDeviceOrientationProvider.m */,
230-
7F29EB282D26A59000740257 /* MockCameraDeviceDiscoverer.m */,
231240
7F29EB272D26A55300740257 /* MockCameraDeviceDiscoverer.h */,
241+
7F29EB282D26A59000740257 /* MockCameraDeviceDiscoverer.m */,
232242
7F29EB3E2D281C5800740257 /* MockCaptureSession.h */,
233243
7F29EB402D281C7E00740257 /* MockCaptureSession.m */,
234-
970ADABD2D6740A900EFDCD9 /* MockWritableData.swift */,
244+
E1FFEAAC2D6C8DD700B14107 /* MockFLTCam.swift */,
235245
970ADABF2D6764CC00EFDCD9 /* MockEventChannel.swift */,
246+
E12C4FF72D68E85500515E70 /* MockFLTCameraPermissionManager.swift */,
247+
970ADABD2D6740A900EFDCD9 /* MockWritableData.swift */,
236248
);
237249
path = Mocks;
238250
sourceTree = "<group>";
@@ -523,6 +535,7 @@
523535
files = (
524536
97BD4A0E2D5CC5AE00F857D5 /* CameraSettingsTests.swift in Sources */,
525537
972CA92D2D5A28C4004B846F /* QueueUtilsTests.swift in Sources */,
538+
E1FFEAB12D6CDE5B00B14107 /* CameraPluginInitializeCameraTests.swift in Sources */,
526539
979B3DFB2D5B6BC7009BDE1A /* ExceptionCatcher.m in Sources */,
527540
7FD83D2B2D5BA65B00F4DB7C /* MockCaptureConnection.m in Sources */,
528541
977A25242D5A511600931E34 /* CameraPermissionTests.swift in Sources */,
@@ -531,16 +544,19 @@
531544
7FD582352D57D97C003B1200 /* MockCaptureDeviceFormat.m in Sources */,
532545
979B3DFE2D5B985B009BDE1A /* CameraCaptureSessionQueueRaceConditionTests.swift in Sources */,
533546
7F8FD22F2D4D0B88001AF2C1 /* MockFlutterBinaryMessenger.m in Sources */,
547+
E12C4FF82D68E85500515E70 /* MockFLTCameraPermissionManager.swift in Sources */,
534548
97922B0D2D6380C300A9B4CF /* SampleBufferTests.swift in Sources */,
535549
972CA92B2D5A1D8C004B846F /* CameraPropertiesTests.swift in Sources */,
536550
E0CDBAC227CD9729002561D9 /* CameraTestUtils.m in Sources */,
537551
978296CF2D5F744B0009BDD3 /* PhotoCaptureTests.swift in Sources */,
538552
7FD582202D579ECC003B1200 /* MockCapturePhotoOutput.m in Sources */,
539553
979B3E002D5B9E6C009BDE1A /* CameraMethodChannelTests.swift in Sources */,
554+
E1FFEAAF2D6CDA8C00B14107 /* CameraPluginCreateCameraTests.swift in Sources */,
540555
97DB234D2D566D0700CEFE66 /* CameraPreviewPauseTests.swift in Sources */,
541556
970ADAC02D6764CC00EFDCD9 /* MockEventChannel.swift in Sources */,
542557
977A25202D5A439300931E34 /* AvailableCamerasTests.swift in Sources */,
543558
972CA9312D5A366C004B846F /* CameraExposureTests.swift in Sources */,
559+
E1FFEAAD2D6C8DD700B14107 /* MockFLTCam.swift in Sources */,
544560
7F29EB292D26A59000740257 /* MockCameraDeviceDiscoverer.m in Sources */,
545561
97BD4A102D5CE13500F857D5 /* CameraSessionPresetsTests.swift in Sources */,
546562
7FD582272D57C020003B1200 /* MockAssetWriter.m in Sources */,

packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTests.swift

Lines changed: 80 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ final class AvailableCamerasTest: XCTestCase {
1515
messenger: MockFlutterBinaryMessenger(),
1616
globalAPI: MockGlobalEventApi(),
1717
deviceDiscoverer: deviceDiscoverer,
18+
permissionManager: MockFLTCameraPermissionManager(),
1819
deviceFactory: { _ in MockCaptureDevice() },
1920
captureSessionFactory: { MockCaptureSession() },
2021
captureDeviceInputFactory: MockCaptureDeviceInputFactory()
@@ -26,35 +27,35 @@ final class AvailableCamerasTest: XCTestCase {
2627
let cameraPlugin = createCameraPlugin(with: mockDeviceDiscoverer)
2728
let expectation = self.expectation(description: "Result finished")
2829

29-
// iPhone 13 Cameras:
30-
let wideAngleCamera = MockCaptureDevice()
31-
wideAngleCamera.uniqueID = "0"
32-
wideAngleCamera.position = .back
33-
34-
let frontFacingCamera = MockCaptureDevice()
35-
frontFacingCamera.uniqueID = "1"
36-
frontFacingCamera.position = .front
37-
38-
let ultraWideCamera = MockCaptureDevice()
39-
ultraWideCamera.uniqueID = "2"
40-
ultraWideCamera.position = .back
41-
42-
let telephotoCamera = MockCaptureDevice()
43-
telephotoCamera.uniqueID = "3"
44-
telephotoCamera.position = .back
45-
46-
var requiredTypes: [AVCaptureDevice.DeviceType] = [
47-
.builtInWideAngleCamera, .builtInTelephotoCamera,
48-
]
49-
if #available(iOS 13.0, *) {
50-
requiredTypes.append(.builtInUltraWideCamera)
51-
}
52-
var cameras: [MockCaptureDevice] = [wideAngleCamera, frontFacingCamera, telephotoCamera]
53-
if #available(iOS 13.0, *) {
54-
cameras.append(ultraWideCamera)
55-
}
56-
5730
mockDeviceDiscoverer.discoverySessionStub = { deviceTypes, mediaType, position in
31+
// iPhone 13 Cameras:
32+
let wideAngleCamera = MockCaptureDevice()
33+
wideAngleCamera.uniqueID = "0"
34+
wideAngleCamera.position = .back
35+
36+
let frontFacingCamera = MockCaptureDevice()
37+
frontFacingCamera.uniqueID = "1"
38+
frontFacingCamera.position = .front
39+
40+
let ultraWideCamera = MockCaptureDevice()
41+
ultraWideCamera.uniqueID = "2"
42+
ultraWideCamera.position = .back
43+
44+
let telephotoCamera = MockCaptureDevice()
45+
telephotoCamera.uniqueID = "3"
46+
telephotoCamera.position = .back
47+
48+
var requiredTypes: [AVCaptureDevice.DeviceType] = [
49+
.builtInWideAngleCamera, .builtInTelephotoCamera,
50+
]
51+
if #available(iOS 13.0, *) {
52+
requiredTypes.append(.builtInUltraWideCamera)
53+
}
54+
var cameras = [wideAngleCamera, frontFacingCamera, telephotoCamera]
55+
if #available(iOS 13.0, *) {
56+
cameras.append(ultraWideCamera)
57+
}
58+
5859
XCTAssertEqual(deviceTypes, requiredTypes)
5960
XCTAssertEqual(mediaType, .video)
6061
XCTAssertEqual(position, .unspecified)
@@ -77,29 +78,65 @@ final class AvailableCamerasTest: XCTestCase {
7778
}
7879
}
7980

80-
func testAvailableCamerasShouldReturnOneCameraOnSingleCameraIPhone() {
81+
func testAvailableCamerasShouldReturnTwoCamerasOnDualCameraIPhone() {
8182
let mockDeviceDiscoverer = MockCameraDeviceDiscoverer()
8283
let cameraPlugin = createCameraPlugin(with: mockDeviceDiscoverer)
8384
let expectation = self.expectation(description: "Result finished")
8485

85-
// iPhone 8 Cameras:
86-
let wideAngleCamera = MockCaptureDevice()
87-
wideAngleCamera.uniqueID = "0"
88-
wideAngleCamera.position = .back
86+
mockDeviceDiscoverer.discoverySessionStub = { deviceTypes, mediaType, position in
87+
// iPhone 8 Cameras:
88+
let wideAngleCamera = MockCaptureDevice()
89+
wideAngleCamera.uniqueID = "0"
90+
wideAngleCamera.position = .back
91+
92+
let frontFacingCamera = MockCaptureDevice()
93+
frontFacingCamera.uniqueID = "1"
94+
frontFacingCamera.position = .front
95+
96+
var requiredTypes: [AVCaptureDevice.DeviceType] = [
97+
.builtInWideAngleCamera, .builtInTelephotoCamera,
98+
]
99+
if #available(iOS 13.0, *) {
100+
requiredTypes.append(.builtInUltraWideCamera)
101+
}
102+
let cameras = [wideAngleCamera, frontFacingCamera]
89103

90-
let frontFacingCamera = MockCaptureDevice()
91-
frontFacingCamera.uniqueID = "1"
92-
frontFacingCamera.position = .front
104+
XCTAssertEqual(deviceTypes, requiredTypes)
105+
XCTAssertEqual(mediaType, .video)
106+
XCTAssertEqual(position, .unspecified)
107+
return cameras
108+
}
93109

94-
var requiredTypes: [AVCaptureDevice.DeviceType] = [
95-
.builtInWideAngleCamera, .builtInTelephotoCamera,
96-
]
97-
if #available(iOS 13.0, *) {
98-
requiredTypes.append(.builtInUltraWideCamera)
110+
var resultValue: [FCPPlatformCameraDescription]?
111+
cameraPlugin.availableCameras { result, error in
112+
XCTAssertNil(error)
113+
resultValue = result
114+
expectation.fulfill()
99115
}
100-
let cameras: [MockCaptureDevice] = [wideAngleCamera, frontFacingCamera]
116+
waitForExpectations(timeout: 30, handler: nil)
117+
118+
// Verify the result.
119+
XCTAssertEqual(resultValue?.count, 2)
120+
}
121+
122+
func testAvailableCamerasShouldReturnExternalLensDirectionForUnspecifiedCameraPosition() {
123+
let mockDeviceDiscoverer = MockCameraDeviceDiscoverer()
124+
let cameraPlugin = createCameraPlugin(with: mockDeviceDiscoverer)
125+
let expectation = self.expectation(description: "Result finished")
101126

102127
mockDeviceDiscoverer.discoverySessionStub = { deviceTypes, mediaType, position in
128+
let unspecifiedCamera = MockCaptureDevice()
129+
unspecifiedCamera.uniqueID = "0"
130+
unspecifiedCamera.position = .unspecified
131+
132+
var requiredTypes: [AVCaptureDevice.DeviceType] = [
133+
.builtInWideAngleCamera, .builtInTelephotoCamera,
134+
]
135+
if #available(iOS 13.0, *) {
136+
requiredTypes.append(.builtInUltraWideCamera)
137+
}
138+
let cameras = [unspecifiedCamera]
139+
103140
XCTAssertEqual(deviceTypes, requiredTypes)
104141
XCTAssertEqual(mediaType, .video)
105142
XCTAssertEqual(position, .unspecified)
@@ -114,7 +151,6 @@ final class AvailableCamerasTest: XCTestCase {
114151
}
115152
waitForExpectations(timeout: 30, handler: nil)
116153

117-
// Verify the result.
118-
XCTAssertEqual(resultValue?.count, 2)
154+
XCTAssertEqual(resultValue?.first?.lensDirection, .external)
119155
}
120156
}

packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraCaptureSessionQueueRaceConditionTests.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ final class CameraCaptureSessionQueueRaceConditionTests: XCTestCase {
1313
messenger: MockFlutterBinaryMessenger(),
1414
globalAPI: MockGlobalEventApi(),
1515
deviceDiscoverer: MockCameraDeviceDiscoverer(),
16+
permissionManager: MockFLTCameraPermissionManager(),
1617
deviceFactory: { _ in MockCaptureDevice() },
1718
captureSessionFactory: { MockCaptureSession() },
1819
captureDeviceInputFactory: MockCaptureDeviceInputFactory()

packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraMethodChannelTests.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ final class CameraMethodChannelTests: XCTestCase {
1414
messenger: MockFlutterBinaryMessenger(),
1515
globalAPI: MockGlobalEventApi(),
1616
deviceDiscoverer: MockCameraDeviceDiscoverer(),
17+
permissionManager: MockFLTCameraPermissionManager(),
1718
deviceFactory: { _ in MockCaptureDevice() },
1819
captureSessionFactory: { session },
1920
captureDeviceInputFactory: MockCaptureDeviceInputFactory()

packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraOrientationTests.swift

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,6 @@ import XCTest
88

99
@testable import camera_avfoundation
1010

11-
private final class MockCamera: FLTCam {
12-
var setDeviceOrientationStub: ((UIDeviceOrientation) -> Void)?
13-
14-
override func setDeviceOrientation(_ orientation: UIDeviceOrientation) {
15-
setDeviceOrientationStub?(orientation)
16-
}
17-
}
18-
1911
private final class MockUIDevice: UIDevice {
2012
var mockOrientation: UIDeviceOrientation = .unknown
2113

@@ -26,10 +18,10 @@ private final class MockUIDevice: UIDevice {
2618

2719
final class CameraOrientationTests: XCTestCase {
2820
private func createCameraPlugin() -> (
29-
CameraPlugin, MockCamera, MockGlobalEventApi, MockCaptureDevice, MockCameraDeviceDiscoverer
21+
CameraPlugin, MockFLTCam, MockGlobalEventApi, MockCaptureDevice, MockCameraDeviceDiscoverer
3022
) {
3123
let mockDevice = MockCaptureDevice()
32-
let mockCamera = MockCamera()
24+
let mockCamera = MockFLTCam()
3325
let mockEventAPI = MockGlobalEventApi()
3426
let mockDeviceDiscoverer = MockCameraDeviceDiscoverer()
3527

@@ -38,6 +30,7 @@ final class CameraOrientationTests: XCTestCase {
3830
messenger: MockFlutterBinaryMessenger(),
3931
globalAPI: mockEventAPI,
4032
deviceDiscoverer: mockDeviceDiscoverer,
33+
permissionManager: MockFLTCameraPermissionManager(),
4134
deviceFactory: { _ in mockDevice },
4235
captureSessionFactory: { MockCaptureSession() },
4336
captureDeviceInputFactory: MockCaptureDeviceInputFactory()
@@ -124,6 +117,7 @@ final class CameraOrientationTests: XCTestCase {
124117
messenger: MockFlutterBinaryMessenger(),
125118
globalAPI: mockEventAPI,
126119
deviceDiscoverer: mockDeviceDiscoverer,
120+
permissionManager: MockFLTCameraPermissionManager(),
127121
deviceFactory: { _ in weakDevice! },
128122
captureSessionFactory: { MockCaptureSession() },
129123
captureDeviceInputFactory: MockCaptureDeviceInputFactory()

0 commit comments

Comments
 (0)