From 8f8c8c57952288934c49b93b863a4ac8bdeb92bc Mon Sep 17 00:00:00 2001 From: Robert Odrowaz Date: Mon, 28 Apr 2025 17:32:26 +0200 Subject: [PATCH 1/3] Add test FLTCam creation util method --- .../RunnerTests/CameraPreviewPauseTests.swift | 4 ++-- .../CameraSessionPresetsTests.swift | 6 ++--- .../ios/RunnerTests/CameraSettingsTests.swift | 4 ++-- .../ios/RunnerTests/CameraTestUtils.swift | 22 +++++++++++++------ .../ios/RunnerTests/FLTCamExposureTests.swift | 2 +- .../ios/RunnerTests/FLTCamFocusTests.swift | 2 +- .../FLTCamSetDeviceOrientationTests.swift | 3 +-- .../RunnerTests/FLTCamSetFlashModeTests.swift | 2 +- .../ios/RunnerTests/FLTCamZoomTests.swift | 2 +- .../ios/RunnerTests/PhotoCaptureTests.swift | 4 ++-- .../ios/RunnerTests/StreamingTests.swift | 2 +- 11 files changed, 30 insertions(+), 23 deletions(-) diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.swift index d4e7eaf06e27..306b299e0eda 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.swift @@ -14,7 +14,7 @@ import XCTest final class CameraPreviewPauseTests: XCTestCase { func testPausePreviewWithResult_shouldPausePreview() { - let camera = FLTCam() + let camera = CameraTestUtils.createTestCamera() camera.pausePreview() @@ -22,7 +22,7 @@ final class CameraPreviewPauseTests: XCTestCase { } func testResumePreviewWithResult_shouldResumePreview() { - let camera = FLTCam() + let camera = CameraTestUtils.createTestCamera() camera.resumePreview() diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraSessionPresetsTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraSessionPresetsTests.swift index c8ed7814251b..bc1dd5b55a02 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraSessionPresetsTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraSessionPresetsTests.swift @@ -43,7 +43,7 @@ final class CameraSessionPresetsTests: XCTestCase { configuration.mediaSettings = CameraTestUtils.createDefaultMediaSettings( resolutionPreset: FCPPlatformResolutionPreset.max) - let _ = FLTCam(configuration: configuration, error: nil) + let _ = CameraTestUtils.createTestCamera(configuration) waitForExpectations(timeout: 30, handler: nil) } @@ -67,7 +67,7 @@ final class CameraSessionPresetsTests: XCTestCase { resolutionPreset: FCPPlatformResolutionPreset.max) configuration.captureDeviceFactory = { _ in MockCaptureDevice() } - let _ = FLTCam(configuration: configuration, error: nil) + let _ = CameraTestUtils.createTestCamera(configuration) waitForExpectations(timeout: 30, handler: nil) } @@ -91,7 +91,7 @@ final class CameraSessionPresetsTests: XCTestCase { configuration.mediaSettings = CameraTestUtils.createDefaultMediaSettings( resolutionPreset: FCPPlatformResolutionPreset.ultraHigh) - let _ = FLTCam(configuration: configuration, error: nil) + let _ = CameraTestUtils.createTestCamera(configuration) waitForExpectations(timeout: 30, handler: nil) } diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraSettingsTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraSettingsTests.swift index aedb0a21a73a..6fd4e0da767e 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraSettingsTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraSettingsTests.swift @@ -126,7 +126,7 @@ final class CameraSettingsTests: XCTestCase { let configuration = CameraTestUtils.createTestCameraConfiguration() configuration.mediaSettingsWrapper = injectedWrapper configuration.mediaSettings = settings - let camera = FLTCam(configuration: configuration, error: nil) + let camera = CameraTestUtils.createTestCamera(configuration) // Expect FPS configuration is passed to camera device. wait( @@ -200,7 +200,7 @@ final class CameraSettingsTests: XCTestCase { let configuration = CameraTestUtils.createTestCameraConfiguration() configuration.mediaSettings = settings - let camera = FLTCam(configuration: configuration, error: nil) + let camera = CameraTestUtils.createTestCamera(configuration) let range = camera.captureDevice.activeFormat.videoSupportedFrameRateRanges[0] XCTAssertLessThanOrEqual(range.minFrameRate, 60) diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.swift index 46e443d4e65f..04e302551ecf 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.swift @@ -80,10 +80,20 @@ enum CameraTestUtils { return configuration } - static func createCameraWithCaptureSessionQueue(_ captureSessionQueue: DispatchQueue) -> FLTCam { + static func createTestCamera(_ configuration: FLTCamConfiguration) -> FLTCam { + return FLTCam(configuration: configuration, error: nil) + } + + static func createTestCamera() -> FLTCam { + return CameraTestUtils.createTestCamera(CameraTestUtils.createTestCameraConfiguration()) + } + + static func createCameraWithCaptureSessionQueue(_ captureSessionQueue: DispatchQueue) + -> FLTCam + { let configuration = createTestCameraConfiguration() configuration.captureSessionQueue = captureSessionQueue - return FLTCam(configuration: configuration, error: nil) + return CameraTestUtils.createTestCamera(configuration) } /// Creates a test sample buffer. @@ -116,7 +126,7 @@ enum CameraTestUtils { /// Creates a test audio sample buffer. /// @return a test audio sample buffer. - static func createTestAudioSampleBuffer() -> CMSampleBuffer? { + static func createTestAudioSampleBuffer() -> CMSampleBuffer { var blockBuffer: CMBlockBuffer? CMBlockBufferCreateWithMemoryBlock( allocator: kCFAllocatorDefault, @@ -129,8 +139,6 @@ enum CameraTestUtils { flags: kCMBlockBufferAssureMemoryNowFlag, blockBufferOut: &blockBuffer) - guard let blockBuffer = blockBuffer else { return nil } - var formatDescription: CMFormatDescription? var basicDescription = AudioStreamBasicDescription( mSampleRate: 44100, @@ -156,13 +164,13 @@ enum CameraTestUtils { var sampleBuffer: CMSampleBuffer? CMAudioSampleBufferCreateReadyWithPacketDescriptions( allocator: kCFAllocatorDefault, - dataBuffer: blockBuffer, + dataBuffer: blockBuffer!, formatDescription: formatDescription!, sampleCount: 1, presentationTimeStamp: .zero, packetDescriptions: nil, sampleBufferOut: &sampleBuffer) - return sampleBuffer + return sampleBuffer! } } diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamExposureTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamExposureTests.swift index dc910d42f659..670ae621cb68 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamExposureTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamExposureTests.swift @@ -19,7 +19,7 @@ final class FLTCamExposureTests: XCTestCase { let configuration = CameraTestUtils.createTestCameraConfiguration() configuration.captureDeviceFactory = { _ in mockDevice } configuration.deviceOrientationProvider = mockDeviceOrientationProvider - let camera = FLTCam(configuration: configuration, error: nil) + let camera = CameraTestUtils.createTestCamera(configuration) return (camera, mockDevice, mockDeviceOrientationProvider) } diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamFocusTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamFocusTests.swift index bac25ca2d120..efd7869cbee0 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamFocusTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamFocusTests.swift @@ -20,7 +20,7 @@ final class FLTCamSetFocusModeTests: XCTestCase { let configuration = CameraTestUtils.createTestCameraConfiguration() configuration.captureDeviceFactory = { _ in mockDevice } configuration.deviceOrientationProvider = mockDeviceOrientationProvider - let camera = FLTCam(configuration: configuration, error: nil) + let camera = CameraTestUtils.createTestCamera(configuration) return (camera, mockDevice, mockDeviceOrientationProvider) } diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSetDeviceOrientationTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSetDeviceOrientationTests.swift index 1a915c923bd6..b0baa8ccc83b 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSetDeviceOrientationTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSetDeviceOrientationTests.swift @@ -14,8 +14,7 @@ import XCTest final class FLTCamSetDeviceOrientationTests: XCTestCase { private func createCamera() -> (FLTCam, MockCaptureConnection, MockCaptureConnection) { - let configuration = CameraTestUtils.createTestCameraConfiguration() - let camera = FLTCam(configuration: configuration, error: nil) + let camera = CameraTestUtils.createTestCamera() let mockCapturePhotoOutput = MockCapturePhotoOutput() let mockPhotoCaptureConnection = MockCaptureConnection() diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSetFlashModeTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSetFlashModeTests.swift index ce742ddde5b0..dd06ca592d23 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSetFlashModeTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSetFlashModeTests.swift @@ -19,7 +19,7 @@ final class FLTCamSetFlashModeTests: XCTestCase { let configuration = CameraTestUtils.createTestCameraConfiguration() configuration.captureDeviceFactory = { _ in mockDevice } - let camera = FLTCam(configuration: configuration, error: nil) + let camera = CameraTestUtils.createTestCamera(configuration) camera.capturePhotoOutput = mockCapturePhotoOutput return (camera, mockDevice, mockCapturePhotoOutput) diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamZoomTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamZoomTests.swift index 249871bfa8e3..3e854037e0c8 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamZoomTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamZoomTests.swift @@ -18,7 +18,7 @@ final class FLTCamZoomTests: XCTestCase { let configuration = CameraTestUtils.createTestCameraConfiguration() configuration.captureDeviceFactory = { _ in mockDevice } - let camera = FLTCam(configuration: configuration, error: nil) + let camera = CameraTestUtils.createTestCamera(configuration) return (camera, mockDevice) } diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/PhotoCaptureTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/PhotoCaptureTests.swift index 5e0f487b76c8..e7986fcce729 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/PhotoCaptureTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/PhotoCaptureTests.swift @@ -17,7 +17,7 @@ final class PhotoCaptureTests: XCTestCase { private func createCam(with captureSessionQueue: DispatchQueue) -> FLTCam { let configuration = CameraTestUtils.createTestCameraConfiguration() configuration.captureSessionQueue = captureSessionQueue - return FLTCam(configuration: configuration, error: nil) + return CameraTestUtils.createTestCamera(configuration) } func testCaptureToFile_mustReportErrorToResultIfSavePhotoDelegateCompletionsWithError() { @@ -174,7 +174,7 @@ final class PhotoCaptureTests: XCTestCase { let configuration = CameraTestUtils.createTestCameraConfiguration() configuration.captureSessionQueue = captureSessionQueue configuration.captureDeviceFactory = { _ in captureDeviceMock } - let cam = FLTCam(configuration: configuration, error: nil) + let cam = CameraTestUtils.createTestCamera(configuration) let filePath = "test" let mockOutput = MockCapturePhotoOutput() diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/StreamingTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/StreamingTests.swift index c5e9bab66a4d..2dfbbedf24ac 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/StreamingTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/StreamingTests.swift @@ -37,7 +37,7 @@ final class StreamingTests: XCTestCase { let configuration = CameraTestUtils.createTestCameraConfiguration() configuration.captureSessionQueue = captureSessionQueue - let camera = FLTCam(configuration: configuration, error: nil) + let camera = CameraTestUtils.createTestCamera(configuration) let sampleBuffer = CameraTestUtils.createTestSampleBuffer() return (camera, sampleBuffer) From 89c3992cc513228c6c2e192bfaa2558b83a125bc Mon Sep 17 00:00:00 2001 From: Robert Odrowaz Date: Thu, 1 May 2025 11:28:03 +0200 Subject: [PATCH 2/3] Remove KVO usage from StreamingTests --- .../RunnerTests/CameraOrientationTests.swift | 12 +--------- .../CameraPluginInitializeCameraTests.swift | 12 +--------- .../ios/RunnerTests/CameraTestUtils.swift | 22 ++++++++++++++++++- .../ios/RunnerTests/StreamingTests.swift | 12 ++++------ 4 files changed, 27 insertions(+), 31 deletions(-) diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraOrientationTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraOrientationTests.swift index 3b12f1d7e823..920f9f698209 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraOrientationTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraOrientationTests.swift @@ -59,23 +59,13 @@ final class CameraOrientationTests: XCTestCase { ) } - private func waitForRoundTrip(with queue: DispatchQueue) { - let expectation = self.expectation(description: "Queue flush") - queue.async { - DispatchQueue.main.async { - expectation.fulfill() - } - } - waitForExpectations(timeout: 30, handler: nil) - } - private func sendOrientation( _ orientation: UIDeviceOrientation, to cameraPlugin: CameraPlugin, captureSessionQueue: DispatchQueue ) { cameraPlugin.orientationChanged(createMockNotification(for: orientation)) - waitForRoundTrip(with: captureSessionQueue) + waitForQueueRoundTrip(with: captureSessionQueue) } private func createMockNotification(for deviceOrientation: UIDeviceOrientation) -> Notification { diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPluginInitializeCameraTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPluginInitializeCameraTests.swift index 87b61050d395..6a8a2906147a 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPluginInitializeCameraTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPluginInitializeCameraTests.swift @@ -35,16 +35,6 @@ final class CameraPluginInitializeCameraTests: XCTestCase { return (cameraPlugin, mockCamera, mockGlobalEventApi, captureSessionQueue) } - private func waitForRoundTrip(with queue: DispatchQueue) { - let expectation = self.expectation(description: "Queue flush") - queue.async { - DispatchQueue.main.async { - expectation.fulfill() - } - } - waitForExpectations(timeout: 30, handler: nil) - } - func testInitializeCamera_setsCameraOnFrameAvailableCallback() { let (cameraPlugin, mockCamera, _, _) = createCameraPlugin() let expectation = expectation(description: "Initialization completed") @@ -93,7 +83,7 @@ final class CameraPluginInitializeCameraTests: XCTestCase { XCTAssertNil(error) } - waitForRoundTrip(with: captureSessionQueue) + waitForQueueRoundTrip(with: captureSessionQueue) XCTAssertTrue(mockGlobalEventApi.deviceOrientationChangedCalled) } diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.swift index 04e302551ecf..1b7a44355e33 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.swift @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import Foundation +import XCTest // Import Objectice-C part of the implementation when SwiftPM is used. #if canImport(camera_avfoundation_objc) @@ -174,3 +174,23 @@ enum CameraTestUtils { return sampleBuffer! } } + +extension XCTestCase { + /// Wait until a round trip of a given `DispatchQueue` is complete. This allows for testing + /// side-effects of async functions that do not provide any notification of completion. + func waitForQueueRoundTrip(with queue: DispatchQueue) { + let expectation = expectation(description: "Queue flush") + + queue.async { + if queue == DispatchQueue.main { + expectation.fulfill() + } else { + DispatchQueue.main.async { + expectation.fulfill() + } + } + } + + wait(for: [expectation], timeout: 1) + } +} diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/StreamingTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/StreamingTests.swift index 2dfbbedf24ac..3909b5be9c9a 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/StreamingTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/StreamingTests.swift @@ -54,10 +54,8 @@ final class StreamingTests: XCTestCase { let messenger = MockFlutterBinaryMessenger() camera.startImageStream(with: messenger, imageStreamHandler: handlerMock) - let expectation = XCTKVOExpectation( - keyPath: "isStreamingImages", object: camera, expectedValue: true) - let result = XCTWaiter.wait(for: [expectation], timeout: 1) - XCTAssertEqual(result, .completed) + waitForQueueRoundTrip(with: DispatchQueue.main) + XCTAssertEqual(camera.isStreamingImages, true) streamingExpectation.expectedFulfillmentCount = 4 for _ in 0..<10 { @@ -78,10 +76,8 @@ final class StreamingTests: XCTestCase { let messenger = MockFlutterBinaryMessenger() camera.startImageStream(with: messenger, imageStreamHandler: handlerMock) - let expectation = XCTKVOExpectation( - keyPath: "isStreamingImages", object: camera, expectedValue: true) - let result = XCTWaiter.wait(for: [expectation], timeout: 1) - XCTAssertEqual(result, .completed) + waitForQueueRoundTrip(with: DispatchQueue.main) + XCTAssertEqual(camera.isStreamingImages, true) streamingExpectation.expectedFulfillmentCount = 5 for _ in 0..<10 { From d9d94d860a2c0322be2fd3bd6124729017de7556 Mon Sep 17 00:00:00 2001 From: Robert Odrowaz Date: Wed, 7 May 2025 06:51:28 +0200 Subject: [PATCH 3/3] Remove unnecessary verbose static method calls in CameraTestUtils --- .../example/ios/RunnerTests/CameraTestUtils.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.swift index 1b7a44355e33..b624c8d56920 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.swift @@ -85,7 +85,7 @@ enum CameraTestUtils { } static func createTestCamera() -> FLTCam { - return CameraTestUtils.createTestCamera(CameraTestUtils.createTestCameraConfiguration()) + return createTestCamera(createTestCameraConfiguration()) } static func createCameraWithCaptureSessionQueue(_ captureSessionQueue: DispatchQueue) @@ -93,7 +93,7 @@ enum CameraTestUtils { { let configuration = createTestCameraConfiguration() configuration.captureSessionQueue = captureSessionQueue - return CameraTestUtils.createTestCamera(configuration) + return createTestCamera(configuration) } /// Creates a test sample buffer.