Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Backfill tests for CameraPlugin class
  • Loading branch information
RobertOdrowaz committed Mar 3, 2025
commit 73d41b60682365dc96397e9cdb54978d73b4b5f7
5 changes: 5 additions & 0 deletions packages/camera/camera_avfoundation/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 0.9.18+9

* Backfills unit tests for `CameraPlugin` class.
* Adds `minimumExposureOffset` and `maximumExposureOffset` methods to `FLTCam` class.

## 0.9.18+8

* Migrates unit tests to Swift.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
97DB234D2D566D0700CEFE66 /* CameraPreviewPauseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97DB234C2D566D0700CEFE66 /* CameraPreviewPauseTests.swift */; };
E0CDBAC227CD9729002561D9 /* CameraTestUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = E0CDBAC127CD9729002561D9 /* CameraTestUtils.m */; };
E12C4FF62D68C69000515E70 /* CameraPluginForwardingMethodTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12C4FF52D68C69000515E70 /* CameraPluginForwardingMethodTests.swift */; };
E12C4FF82D68E85500515E70 /* MockFLTCameraPermissionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12C4FF72D68E85500515E70 /* MockFLTCameraPermissionManager.swift */; };
E1FFEAAD2D6C8DD700B14107 /* MockFLTCam.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1FFEAAC2D6C8DD700B14107 /* MockFLTCam.swift */; };
E1FFEAAF2D6CDA8C00B14107 /* CameraPluginCreateCameraTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1FFEAAE2D6CDA8C00B14107 /* CameraPluginCreateCameraTests.swift */; };
E1FFEAB12D6CDE5B00B14107 /* CameraPluginInitializeCameraTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1FFEAB02D6CDE5B00B14107 /* CameraPluginInitializeCameraTests.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -141,6 +146,11 @@
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>"; };
E0CDBAC027CD9729002561D9 /* CameraTestUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CameraTestUtils.h; sourceTree = "<group>"; };
E0CDBAC127CD9729002561D9 /* CameraTestUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraTestUtils.m; sourceTree = "<group>"; };
E12C4FF52D68C69000515E70 /* CameraPluginForwardingMethodTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraPluginForwardingMethodTests.swift; sourceTree = "<group>"; };
E12C4FF72D68E85500515E70 /* MockFLTCameraPermissionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockFLTCameraPermissionManager.swift; sourceTree = "<group>"; };
E1FFEAAC2D6C8DD700B14107 /* MockFLTCam.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockFLTCam.swift; sourceTree = "<group>"; };
E1FFEAAE2D6CDA8C00B14107 /* CameraPluginCreateCameraTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraPluginCreateCameraTests.swift; sourceTree = "<group>"; };
E1FFEAB02D6CDE5B00B14107 /* CameraPluginInitializeCameraTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraPluginInitializeCameraTests.swift; sourceTree = "<group>"; };
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>"; };
ECAF63F924EFA2D68883BA85 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
Expand Down Expand Up @@ -193,6 +203,9 @@
978D90B32D5F630300CD817E /* StreamingTests.swift */,
97922B0C2D6380C300A9B4CF /* SampleBufferTests.swift */,
978296CE2D5F744B0009BDD3 /* PhotoCaptureTests.swift */,
E12C4FF52D68C69000515E70 /* CameraPluginForwardingMethodTests.swift */,
E1FFEAAE2D6CDA8C00B14107 /* CameraPluginCreateCameraTests.swift */,
E1FFEAB02D6CDE5B00B14107 /* CameraPluginInitializeCameraTests.swift */,
);
path = RunnerTests;
sourceTree = "<group>";
Expand All @@ -215,8 +228,8 @@
7F8FD22E2D4D0B88001AF2C1 /* MockFlutterBinaryMessenger.m */,
7F8FD22A2D4D07A6001AF2C1 /* MockFlutterTextureRegistry.h */,
7F8FD22B2D4D07DD001AF2C1 /* MockFlutterTextureRegistry.m */,
7F8FD2282D4BFABF001AF2C1 /* MockGlobalEventApi.m */,
7F8FD2272D4BFA8D001AF2C1 /* MockGlobalEventApi.h */,
7F8FD2282D4BFABF001AF2C1 /* MockGlobalEventApi.m */,
7FD83D292D5BA49100F4DB7C /* MockCaptureConnection.h */,
7FD83D2A2D5BA65B00F4DB7C /* MockCaptureConnection.m */,
7FCEDD312D43C2B900EA1CA8 /* MockCaptureDevice.h */,
Expand All @@ -227,12 +240,14 @@
7FD5821F2D579ECC003B1200 /* MockCapturePhotoOutput.m */,
7FCEDD332D43C2B900EA1CA8 /* MockDeviceOrientationProvider.h */,
7FCEDD342D43C2B900EA1CA8 /* MockDeviceOrientationProvider.m */,
7F29EB282D26A59000740257 /* MockCameraDeviceDiscoverer.m */,
7F29EB272D26A55300740257 /* MockCameraDeviceDiscoverer.h */,
7F29EB282D26A59000740257 /* MockCameraDeviceDiscoverer.m */,
7F29EB3E2D281C5800740257 /* MockCaptureSession.h */,
7F29EB402D281C7E00740257 /* MockCaptureSession.m */,
970ADABD2D6740A900EFDCD9 /* MockWritableData.swift */,
E1FFEAAC2D6C8DD700B14107 /* MockFLTCam.swift */,
970ADABF2D6764CC00EFDCD9 /* MockEventChannel.swift */,
E12C4FF72D68E85500515E70 /* MockFLTCameraPermissionManager.swift */,
970ADABD2D6740A900EFDCD9 /* MockWritableData.swift */,
);
path = Mocks;
sourceTree = "<group>";
Expand Down Expand Up @@ -523,6 +538,7 @@
files = (
97BD4A0E2D5CC5AE00F857D5 /* CameraSettingsTests.swift in Sources */,
972CA92D2D5A28C4004B846F /* QueueUtilsTests.swift in Sources */,
E1FFEAB12D6CDE5B00B14107 /* CameraPluginInitializeCameraTests.swift in Sources */,
979B3DFB2D5B6BC7009BDE1A /* ExceptionCatcher.m in Sources */,
7FD83D2B2D5BA65B00F4DB7C /* MockCaptureConnection.m in Sources */,
977A25242D5A511600931E34 /* CameraPermissionTests.swift in Sources */,
Expand All @@ -531,20 +547,24 @@
7FD582352D57D97C003B1200 /* MockCaptureDeviceFormat.m in Sources */,
979B3DFE2D5B985B009BDE1A /* CameraCaptureSessionQueueRaceConditionTests.swift in Sources */,
7F8FD22F2D4D0B88001AF2C1 /* MockFlutterBinaryMessenger.m in Sources */,
E12C4FF82D68E85500515E70 /* MockFLTCameraPermissionManager.swift in Sources */,
97922B0D2D6380C300A9B4CF /* SampleBufferTests.swift in Sources */,
972CA92B2D5A1D8C004B846F /* CameraPropertiesTests.swift in Sources */,
E0CDBAC227CD9729002561D9 /* CameraTestUtils.m in Sources */,
978296CF2D5F744B0009BDD3 /* PhotoCaptureTests.swift in Sources */,
7FD582202D579ECC003B1200 /* MockCapturePhotoOutput.m in Sources */,
979B3E002D5B9E6C009BDE1A /* CameraMethodChannelTests.swift in Sources */,
E1FFEAAF2D6CDA8C00B14107 /* CameraPluginCreateCameraTests.swift in Sources */,
97DB234D2D566D0700CEFE66 /* CameraPreviewPauseTests.swift in Sources */,
970ADAC02D6764CC00EFDCD9 /* MockEventChannel.swift in Sources */,
977A25202D5A439300931E34 /* AvailableCamerasTests.swift in Sources */,
972CA9312D5A366C004B846F /* CameraExposureTests.swift in Sources */,
E1FFEAAD2D6C8DD700B14107 /* MockFLTCam.swift in Sources */,
7F29EB292D26A59000740257 /* MockCameraDeviceDiscoverer.m in Sources */,
97BD4A102D5CE13500F857D5 /* CameraSessionPresetsTests.swift in Sources */,
7FD582272D57C020003B1200 /* MockAssetWriter.m in Sources */,
979B3E022D5BA48F009BDE1A /* CameraOrientationTests.swift in Sources */,
E12C4FF62D68C69000515E70 /* CameraPluginForwardingMethodTests.swift in Sources */,
977A25222D5A49EC00931E34 /* CameraFocusTests.swift in Sources */,
978D90B42D5F630300CD817E /* StreamingTests.swift in Sources */,
7F29EB412D281C7E00740257 /* MockCaptureSession.m in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ final class AvailableCamerasTest: XCTestCase {
messenger: MockFlutterBinaryMessenger(),
globalAPI: MockGlobalEventApi(),
deviceDiscoverer: deviceDiscoverer,
permissionManager: MockFLTCameraPermissionManager(),
deviceFactory: { _ in MockCaptureDevice() },
captureSessionFactory: { MockCaptureSession() },
captureDeviceInputFactory: MockCaptureDeviceInputFactory()
Expand Down Expand Up @@ -77,7 +78,7 @@ final class AvailableCamerasTest: XCTestCase {
}
}

func testAvailableCamerasShouldReturnOneCameraOnSingleCameraIPhone() {
func testAvailableCamerasShouldReturnTwoCamerasOnDualCameraIPhone() {
let mockDeviceDiscoverer = MockCameraDeviceDiscoverer()
let cameraPlugin = createCameraPlugin(with: mockDeviceDiscoverer)
let expectation = self.expectation(description: "Result finished")
Expand Down Expand Up @@ -117,4 +118,39 @@ final class AvailableCamerasTest: XCTestCase {
// Verify the result.
XCTAssertEqual(resultValue?.count, 2)
}

func testAvailableCamerasShouldReturnExternalLensDirectionForUnspecifiedCameraPosition() {
let mockDeviceDiscoverer = MockCameraDeviceDiscoverer()
let cameraPlugin = createCameraPlugin(with: mockDeviceDiscoverer)
let expectation = self.expectation(description: "Result finished")

let unspecifiedCamera = MockCaptureDevice()
unspecifiedCamera.uniqueID = "0"
unspecifiedCamera.position = .unspecified

var requiredTypes: [AVCaptureDevice.DeviceType] = [
.builtInWideAngleCamera, .builtInTelephotoCamera,
]
if #available(iOS 13.0, *) {
requiredTypes.append(.builtInUltraWideCamera)
}
let cameras: [MockCaptureDevice] = [unspecifiedCamera]

mockDeviceDiscoverer.discoverySessionStub = { deviceTypes, mediaType, position in
XCTAssertEqual(deviceTypes, requiredTypes)
XCTAssertEqual(mediaType, .video)
XCTAssertEqual(position, .unspecified)
return cameras
}

var resultValue: [FCPPlatformCameraDescription]?
cameraPlugin.availableCameras { result, error in
XCTAssertNil(error)
resultValue = result
expectation.fulfill()
}
waitForExpectations(timeout: 30, handler: nil)

XCTAssertEqual(resultValue?.first?.lensDirection, FCPPlatformCameraLensDirection.external)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ final class CameraCaptureSessionQueueRaceConditionTests: XCTestCase {
messenger: MockFlutterBinaryMessenger(),
globalAPI: MockGlobalEventApi(),
deviceDiscoverer: MockCameraDeviceDiscoverer(),
permissionManager: MockFLTCameraPermissionManager(),
deviceFactory: { _ in MockCaptureDevice() },
captureSessionFactory: { MockCaptureSession() },
captureDeviceInputFactory: MockCaptureDeviceInputFactory()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ final class CameraMethodChannelTests: XCTestCase {
messenger: MockFlutterBinaryMessenger(),
globalAPI: MockGlobalEventApi(),
deviceDiscoverer: MockCameraDeviceDiscoverer(),
permissionManager: MockFLTCameraPermissionManager(),
deviceFactory: { _ in MockCaptureDevice() },
captureSessionFactory: { session },
captureDeviceInputFactory: MockCaptureDeviceInputFactory()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,6 @@ import XCTest

@testable import camera_avfoundation

private final class MockCamera: FLTCam {
var setDeviceOrientationStub: ((UIDeviceOrientation) -> Void)?

override func setDeviceOrientation(_ orientation: UIDeviceOrientation) {
setDeviceOrientationStub?(orientation)
}
}

private final class MockUIDevice: UIDevice {
var mockOrientation: UIDeviceOrientation = .unknown

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

final class CameraOrientationTests: XCTestCase {
private func createCameraPlugin() -> (
CameraPlugin, MockCamera, MockGlobalEventApi, MockCaptureDevice, MockCameraDeviceDiscoverer
CameraPlugin, MockFLTCam, MockGlobalEventApi, MockCaptureDevice, MockCameraDeviceDiscoverer
) {
let mockDevice = MockCaptureDevice()
let mockCamera = MockCamera()
let mockCamera = MockFLTCam()
let mockEventAPI = MockGlobalEventApi()
let mockDeviceDiscoverer = MockCameraDeviceDiscoverer()

Expand All @@ -38,6 +30,7 @@ final class CameraOrientationTests: XCTestCase {
messenger: MockFlutterBinaryMessenger(),
globalAPI: mockEventAPI,
deviceDiscoverer: mockDeviceDiscoverer,
permissionManager: MockFLTCameraPermissionManager(),
deviceFactory: { _ in mockDevice },
captureSessionFactory: { MockCaptureSession() },
captureDeviceInputFactory: MockCaptureDeviceInputFactory()
Expand Down Expand Up @@ -124,6 +117,7 @@ final class CameraOrientationTests: XCTestCase {
messenger: MockFlutterBinaryMessenger(),
globalAPI: mockEventAPI,
deviceDiscoverer: mockDeviceDiscoverer,
permissionManager: MockFLTCameraPermissionManager(),
deviceFactory: { _ in weakDevice! },
captureSessionFactory: { MockCaptureSession() },
captureDeviceInputFactory: MockCaptureDeviceInputFactory()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// 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 XCTest

@testable import camera_avfoundation

final class CameraPluginCreateCameraTests: XCTestCase {
private func createSutAndMocks() -> (
CameraPlugin, MockFLTCameraPermissionManager, MockCaptureSession
) {
let mockPermissionManager = MockFLTCameraPermissionManager()
let mockCaptureSession = MockCaptureSession()

let cameraPlugin = CameraPlugin(
registry: MockFlutterTextureRegistry(),
messenger: MockFlutterBinaryMessenger(),
globalAPI: MockGlobalEventApi(),
deviceDiscoverer: MockCameraDeviceDiscoverer(),
permissionManager: mockPermissionManager,
deviceFactory: { _ in MockCaptureDevice() },
captureSessionFactory: { mockCaptureSession },
captureDeviceInputFactory: MockCaptureDeviceInputFactory()
)

return (cameraPlugin, mockPermissionManager, mockCaptureSession)
}

func testCreateCamera_requestsOnlyCameraPermissionWithAudioDisabled() {
let (cameraPlugin, mockPermissionManager, _) = createSutAndMocks()
let expectation = expectation(description: "Initialization completed")

var requestCameraPermissionCalled = false
mockPermissionManager.requestCameraPermissionStub = { completion in
requestCameraPermissionCalled = true
completion?(nil)
}
var requestAudioPermissionCalled = false
mockPermissionManager.requestAudioPermissionStub = { completion in
requestAudioPermissionCalled = true
completion?(nil)
}

cameraPlugin.createCamera(
withName: "camera_name",
settings: FCPPlatformMediaSettings.make(
with: FCPPlatformResolutionPreset.medium,
framesPerSecond: nil,
videoBitrate: nil,
audioBitrate: nil,
enableAudio: false
)
) { result, error in
expectation.fulfill()
}

waitForExpectations(timeout: 30, handler: nil)

XCTAssertTrue(requestCameraPermissionCalled)
XCTAssertFalse(requestAudioPermissionCalled)
}

func testCreateCamera_requestsCameraAndAudioPermissionWithAudioEnabled() {
let (cameraPlugin, mockPermissionManager, _) = createSutAndMocks()
let expectation = expectation(description: "Initialization completed")

var requestCameraPermissionCalled = false
mockPermissionManager.requestCameraPermissionStub = { completion in
requestCameraPermissionCalled = true
completion?(nil)
}
var requestAudioPermissionCalled = false
mockPermissionManager.requestAudioPermissionStub = { completion in
requestAudioPermissionCalled = true
completion?(nil)
}

cameraPlugin.createCamera(
withName: "camera_name",
settings: FCPPlatformMediaSettings.make(
with: FCPPlatformResolutionPreset.medium,
framesPerSecond: nil,
videoBitrate: nil,
audioBitrate: nil,
enableAudio: true
)
) { result, error in
expectation.fulfill()
}

waitForExpectations(timeout: 30, handler: nil)

XCTAssertTrue(requestCameraPermissionCalled)
XCTAssertTrue(requestAudioPermissionCalled)
}

func testCreateCamera_assignsCamera() {
let (cameraPlugin, mockPermissionManager, mockCaptureSession) = createSutAndMocks()
let expectation = expectation(description: "Initialization completed")

mockPermissionManager.requestCameraPermissionStub = { completion in
completion?(nil)
}
mockPermissionManager.requestAudioPermissionStub = { completion in
completion?(nil)
}
mockCaptureSession.canSetSessionPreset = true

cameraPlugin.createCamera(
withName: "camera_name",
settings: FCPPlatformMediaSettings.make(
with: FCPPlatformResolutionPreset.medium,
framesPerSecond: nil,
videoBitrate: nil,
audioBitrate: nil,
enableAudio: true
)
) { result, error in
expectation.fulfill()
}

waitForExpectations(timeout: 30, handler: nil)

XCTAssertNotNil(cameraPlugin.camera)
}
}
Loading