From f476931d3350865aa5e08ba189a5163b13f514b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Fri, 7 Feb 2025 17:43:54 +0100 Subject: [PATCH 01/25] Add bridging header file --- .../RunnerTests/RunnerTests-Bridging-Header.h | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 packages/camera/camera_avfoundation/example/ios/RunnerTests/RunnerTests-Bridging-Header.h diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/RunnerTests-Bridging-Header.h b/packages/camera/camera_avfoundation/example/ios/RunnerTests/RunnerTests-Bridging-Header.h new file mode 100644 index 00000000000..e243f973e94 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/RunnerTests-Bridging-Header.h @@ -0,0 +1,23 @@ +// 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. + +// FIXME Verify if all these imports are needed + sort imports +#import "camera_avfoundation/FLTCam.h" +#import "camera_avfoundation/FLTCam_Test.h" +#import "camera_avfoundation/CameraPlugin.h" +#import "camera_avfoundation/CameraPlugin_Test.h" +#import "camera_avfoundation/FLTCamConfiguration.h" +#import "camera_avfoundation/FLTSavePhotoDelegate.h" +#import "camera_avfoundation/FLTSavePhotoDelegate_Test.h" +#import "camera_avfoundation/FLTThreadSafeEventChannel.h" + +// Test mocks and protocols +#import "MockGlobalEventApi.h" +#import "MockCaptureDevice.h" +#import "MockCaptureSession.h" +#import "MockCameraDeviceDiscoverer.h" +#import "MockDeviceOrientationProvider.h" +#import "MockFlutterTextureRegistry.h" +#import "MockFlutterBinaryMessenger.h" +#import "MockEventChannel.h" From b0decfb4af1592e0d33bb1cfa9de64989dada71b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Fri, 7 Feb 2025 17:48:00 +0100 Subject: [PATCH 02/25] Migrate CameraPreviewPauseTests to Swift --- .../ios/Runner.xcodeproj/project.pbxproj | 18 ++++++++--- .../ios/RunnerTests/CameraPreviewPauseTests.m | 31 ------------------- .../RunnerTests/CameraPreviewPauseTests.swift | 27 ++++++++++++++++ 3 files changed, 41 insertions(+), 35 deletions(-) delete mode 100644 packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.m create mode 100644 packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.swift diff --git a/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj index 75716ac04cf..1ea957c59ac 100644 --- a/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj @@ -37,6 +37,7 @@ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + 97DB234D2D566D0700CEFE66 /* CameraPreviewPauseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97DB234C2D566D0700CEFE66 /* CameraPreviewPauseTests.swift */; }; CEF6611A2B5E36A500D33FD4 /* CameraSessionPresetsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CEF661192B5E36A500D33FD4 /* CameraSessionPresetsTests.m */; }; E01EE4A82799F3A5008C1950 /* QueueUtilsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E01EE4A72799F3A5008C1950 /* QueueUtilsTests.m */; }; E032F250279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */; }; @@ -46,7 +47,6 @@ E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */; }; E0CDBAC227CD9729002561D9 /* CameraTestUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = E0CDBAC127CD9729002561D9 /* CameraTestUtils.m */; }; E0F95E3D27A32AB900699390 /* CameraPropertiesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */; }; - E487C86026D686A10034AC92 /* CameraPreviewPauseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -119,6 +119,8 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 97DB234B2D566D0600CEFE66 /* RunnerTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RunnerTests-Bridging-Header.h"; sourceTree = ""; }; + 97DB234C2D566D0700CEFE66 /* CameraPreviewPauseTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraPreviewPauseTests.swift; sourceTree = ""; }; 9DDC4CE84A8B378AE4A8CD9C /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; A8F314CD1C64E9257EBC811D /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 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 = ""; }; @@ -133,7 +135,6 @@ E0CDBAC027CD9729002561D9 /* CameraTestUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CameraTestUtils.h; sourceTree = ""; }; E0CDBAC127CD9729002561D9 /* CameraTestUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraTestUtils.m; sourceTree = ""; }; E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraPropertiesTests.m; sourceTree = ""; }; - E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraPreviewPauseTests.m; sourceTree = ""; }; 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 = ""; }; ECAF63F924EFA2D68883BA85 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -178,12 +179,13 @@ E01EE4A72799F3A5008C1950 /* QueueUtilsTests.m */, E0CDBAC027CD9729002561D9 /* CameraTestUtils.h */, E0CDBAC127CD9729002561D9 /* CameraTestUtils.m */, - E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */, E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */, E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */, 788A065927B0E02900533D74 /* StreamingTest.m */, 43ED1536282570DE00EB00DE /* AvailableCamerasTest.m */, CEF661192B5E36A500D33FD4 /* CameraSessionPresetsTests.m */, + 97DB234C2D566D0700CEFE66 /* CameraPreviewPauseTests.swift */, + 97DB234B2D566D0600CEFE66 /* RunnerTests-Bridging-Header.h */, ); path = RunnerTests; sourceTree = ""; @@ -354,6 +356,7 @@ TargetAttributes = { 03BB76672665316900CE5A93 = { CreatedOnToolsVersion = 12.5; + LastSwiftMigration = 1540; ProvisioningStyle = Automatic; TestTargetID = 97C146ED1CF9000F007C117D; }; @@ -519,7 +522,6 @@ 7D5FCCD42AEF9D0200FB7108 /* CameraSettingsTests.m in Sources */, 7F8FD2292D4BFABF001AF2C1 /* MockGlobalEventApi.m in Sources */, 7FA99E592D22C75300582559 /* CameraExposureTests.m in Sources */, - E487C86026D686A10034AC92 /* CameraPreviewPauseTests.m in Sources */, E071CF7427B31DE4006EF3BA /* FLTCamSampleBufferTests.m in Sources */, 7FD582352D57D97C003B1200 /* MockCaptureDeviceFormat.m in Sources */, 7F29EB222D269ED500740257 /* MockEventChannel.m in Sources */, @@ -529,6 +531,7 @@ E0CDBAC227CD9729002561D9 /* CameraTestUtils.m in Sources */, 334733EA2668111C00DCC49E /* CameraOrientationTests.m in Sources */, 7FD582202D579ECC003B1200 /* MockCapturePhotoOutput.m in Sources */, + 97DB234D2D566D0700CEFE66 /* CameraPreviewPauseTests.swift in Sources */, CEF6611A2B5E36A500D33FD4 /* CameraSessionPresetsTests.m in Sources */, E032F250279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m in Sources */, 7F29EB292D26A59000740257 /* MockCameraDeviceDiscoverer.m in Sources */, @@ -590,6 +593,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; @@ -607,6 +611,9 @@ MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "dev.flutter.plugins.cameraExample.camera-exampleTests"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "RunnerTests/RunnerTests-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; }; @@ -619,6 +626,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; @@ -635,6 +643,8 @@ MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "dev.flutter.plugins.cameraExample.camera-exampleTests"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "RunnerTests/RunnerTests-Bridging-Header.h"; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; }; diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.m deleted file mode 100644 index 60dfca54168..00000000000 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.m +++ /dev/null @@ -1,31 +0,0 @@ -// 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 camera_avfoundation; -#if __has_include() -@import camera_avfoundation.Test; -#endif -@import XCTest; -@import AVFoundation; - -@interface CameraPreviewPauseTests : XCTestCase -@end - -@implementation CameraPreviewPauseTests - -- (void)testPausePreviewWithResult_shouldPausePreview { - FLTCam *camera = [[FLTCam alloc] init]; - - [camera pausePreview]; - XCTAssertTrue(camera.isPreviewPaused); -} - -- (void)testResumePreviewWithResult_shouldResumePreview { - FLTCam *camera = [[FLTCam alloc] init]; - - [camera resumePreview]; - XCTAssertFalse(camera.isPreviewPaused); -} - -@end diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.swift new file mode 100644 index 00000000000..da44f9d94a4 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.swift @@ -0,0 +1,27 @@ +// 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 camera_avfoundation +import XCTest +import AVFoundation + +final class CameraPreviewPauseTests: XCTestCase { + private var camera: FLTCam! + + override func setUp() { + camera = FLTCam() + } + + func testPausePreviewWithResult_shouldPausePreview() { + camera.pausePreview() + + XCTAssertTrue(camera.isPreviewPaused) + } + + func testResumePreviewWithResult_shouldResumePreview() { + camera.resumePreview() + + XCTAssertFalse(camera.isPreviewPaused) + } +} From 75dca2461b0d238b457913b25cf0e782ca276779 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Mon, 10 Feb 2025 13:18:46 +0100 Subject: [PATCH 03/25] Migrate CameraPropertiesTests to Swift, format code --- .../RunnerTests/CameraPreviewPauseTests.swift | 12 ++-- .../ios/RunnerTests/CameraPropertiesTests.m | 69 ------------------- .../RunnerTests/CameraPropertiesTests.swift | 58 ++++++++++++++++ 3 files changed, 64 insertions(+), 75 deletions(-) delete mode 100644 packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.m create mode 100644 packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.swift diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.swift index da44f9d94a4..159bfbb99bd 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.swift @@ -2,26 +2,26 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import AVFoundation import camera_avfoundation import XCTest -import AVFoundation final class CameraPreviewPauseTests: XCTestCase { private var camera: FLTCam! - + override func setUp() { camera = FLTCam() } - + func testPausePreviewWithResult_shouldPausePreview() { camera.pausePreview() - + XCTAssertTrue(camera.isPreviewPaused) } - + func testResumePreviewWithResult_shouldResumePreview() { camera.resumePreview() - + XCTAssertFalse(camera.isPreviewPaused) } } diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.m deleted file mode 100644 index 6778efb4132..00000000000 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.m +++ /dev/null @@ -1,69 +0,0 @@ -// 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 camera_avfoundation; -#if __has_include() -@import camera_avfoundation.Test; -#endif -@import AVFoundation; -@import XCTest; - -@interface CameraPropertiesTests : XCTestCase - -@end - -@implementation CameraPropertiesTests - -#pragma mark - flash mode tests - -- (void)testFCPGetAVCaptureFlashModeForPigeonFlashMode { - XCTAssertEqual(AVCaptureFlashModeOff, - FCPGetAVCaptureFlashModeForPigeonFlashMode(FCPPlatformFlashModeOff)); - XCTAssertEqual(AVCaptureFlashModeAuto, - FCPGetAVCaptureFlashModeForPigeonFlashMode(FCPPlatformFlashModeAuto)); - XCTAssertEqual(AVCaptureFlashModeOn, - FCPGetAVCaptureFlashModeForPigeonFlashMode(FCPPlatformFlashModeAlways)); - XCTAssertThrows(FCPGetAVCaptureFlashModeForPigeonFlashMode(FCPPlatformFlashModeTorch)); -} - -#pragma mark - video format tests - -- (void)testFCPGetPixelFormatForPigeonFormat { - XCTAssertEqual(kCVPixelFormatType_32BGRA, - FCPGetPixelFormatForPigeonFormat(FCPPlatformImageFormatGroupBgra8888)); - XCTAssertEqual(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, - FCPGetPixelFormatForPigeonFormat(FCPPlatformImageFormatGroupYuv420)); -} - -#pragma mark - device orientation tests - -- (void)testFCPGetUIDeviceOrientationForPigeonDeviceOrientation { - XCTAssertEqual(UIDeviceOrientationPortraitUpsideDown, - FCPGetUIDeviceOrientationForPigeonDeviceOrientation( - FCPPlatformDeviceOrientationPortraitDown)); - XCTAssertEqual(UIDeviceOrientationLandscapeLeft, - FCPGetUIDeviceOrientationForPigeonDeviceOrientation( - FCPPlatformDeviceOrientationLandscapeLeft)); - XCTAssertEqual(UIDeviceOrientationLandscapeRight, - FCPGetUIDeviceOrientationForPigeonDeviceOrientation( - FCPPlatformDeviceOrientationLandscapeRight)); - XCTAssertEqual(UIDeviceOrientationPortrait, FCPGetUIDeviceOrientationForPigeonDeviceOrientation( - FCPPlatformDeviceOrientationPortraitUp)); -} - -- (void)testFLTGetStringForUIDeviceOrientation { - XCTAssertEqual( - FCPPlatformDeviceOrientationPortraitDown, - FCPGetPigeonDeviceOrientationForOrientation(UIDeviceOrientationPortraitUpsideDown)); - XCTAssertEqual(FCPPlatformDeviceOrientationLandscapeLeft, - FCPGetPigeonDeviceOrientationForOrientation(UIDeviceOrientationLandscapeLeft)); - XCTAssertEqual(FCPPlatformDeviceOrientationLandscapeRight, - FCPGetPigeonDeviceOrientationForOrientation(UIDeviceOrientationLandscapeRight)); - XCTAssertEqual(FCPPlatformDeviceOrientationPortraitUp, - FCPGetPigeonDeviceOrientationForOrientation(UIDeviceOrientationPortrait)); - XCTAssertEqual(FCPPlatformDeviceOrientationPortraitUp, - FCPGetPigeonDeviceOrientationForOrientation(-1)); -} - -@end diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.swift new file mode 100644 index 00000000000..72a5ef17a96 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.swift @@ -0,0 +1,58 @@ +// 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 AVFoundation +import camera_avfoundation +import XCTest + +final class CameraPropertiesTests: XCTestCase { + // MARK: - Flash Mode Tests + + func testGetAVCaptureFlashModeForPigeonFlashMode() { + XCTAssertEqual(AVCaptureDevice.FlashMode.off, + FCPGetAVCaptureFlashModeForPigeonFlashMode(FCPPlatformFlashMode.off)) + XCTAssertEqual(AVCaptureDevice.FlashMode.auto, + FCPGetAVCaptureFlashModeForPigeonFlashMode(FCPPlatformFlashMode.auto)) + XCTAssertEqual(AVCaptureDevice.FlashMode.on, + FCPGetAVCaptureFlashModeForPigeonFlashMode(FCPPlatformFlashMode.always)) + // FIXME: Migrate implementation to Swift error to test this + XCTAssertThrowsError(FCPGetAVCaptureFlashModeForPigeonFlashMode(FCPPlatformFlashMode.torch)) + } + + // MARK: - Video Format Tests + + func testGetPixelFormatForPigeonFormat() { + XCTAssertEqual(kCVPixelFormatType_32BGRA, + FCPGetPixelFormatForPigeonFormat(FCPPlatformImageFormatGroup.bgra8888)) + XCTAssertEqual(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, + FCPGetPixelFormatForPigeonFormat(FCPPlatformImageFormatGroup.yuv420)) + } + + // MARK: - Device Orientation Tests + + func testGetUIDeviceOrientationForPigeonDeviceOrientation() { + XCTAssertEqual(UIDeviceOrientation.portraitUpsideDown, + FCPGetUIDeviceOrientationForPigeonDeviceOrientation(FCPPlatformDeviceOrientation.portraitDown)) + XCTAssertEqual(UIDeviceOrientation.landscapeLeft, + FCPGetUIDeviceOrientationForPigeonDeviceOrientation(FCPPlatformDeviceOrientation.landscapeLeft)) + XCTAssertEqual(UIDeviceOrientation.landscapeRight, + FCPGetUIDeviceOrientationForPigeonDeviceOrientation(FCPPlatformDeviceOrientation.landscapeRight)) + XCTAssertEqual(UIDeviceOrientation.portrait, + FCPGetUIDeviceOrientationForPigeonDeviceOrientation(FCPPlatformDeviceOrientation.portraitUp)) + } + + func testGetPigeonDeviceOrientationForUIDeviceOrientation() { + XCTAssertEqual(FCPPlatformDeviceOrientation.portraitDown, + FCPGetPigeonDeviceOrientationForOrientation(UIDeviceOrientation.portraitUpsideDown)) + XCTAssertEqual(FCPPlatformDeviceOrientation.landscapeLeft, + FCPGetPigeonDeviceOrientationForOrientation(UIDeviceOrientation.landscapeLeft)) + XCTAssertEqual(FCPPlatformDeviceOrientation.landscapeRight, + FCPGetPigeonDeviceOrientationForOrientation(UIDeviceOrientation.landscapeRight)) + XCTAssertEqual(FCPPlatformDeviceOrientation.portraitUp, + FCPGetPigeonDeviceOrientationForOrientation(UIDeviceOrientation.portrait)) + // Test default case. + XCTAssertEqual(FCPPlatformDeviceOrientation.portraitUp, + FCPGetPigeonDeviceOrientationForOrientation(UIDeviceOrientation.unknown)) + } +} From 4a981120d12d3b7637761dea14d07ec937b94a15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Mon, 10 Feb 2025 13:42:20 +0100 Subject: [PATCH 04/25] Migrate QueueUtilsTests to Swift --- .../example/ios/RunnerTests/QueueUtilsTests.m | 41 ------------------- .../ios/RunnerTests/QueueUtilsTests.swift | 34 +++++++++++++++ 2 files changed, 34 insertions(+), 41 deletions(-) delete mode 100644 packages/camera/camera_avfoundation/example/ios/RunnerTests/QueueUtilsTests.m create mode 100644 packages/camera/camera_avfoundation/example/ios/RunnerTests/QueueUtilsTests.swift diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/QueueUtilsTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/QueueUtilsTests.m deleted file mode 100644 index 8bcf5045787..00000000000 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/QueueUtilsTests.m +++ /dev/null @@ -1,41 +0,0 @@ -// 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 camera_avfoundation; -#if __has_include() -@import camera_avfoundation.Test; -#endif -@import XCTest; - -@interface QueueUtilsTests : XCTestCase - -@end - -@implementation QueueUtilsTests - -- (void)testShouldStayOnMainQueueIfCalledFromMainQueue { - XCTestExpectation *expectation = - [self expectationWithDescription:@"Block must be run on the main queue."]; - FLTEnsureToRunOnMainQueue(^{ - if (NSThread.isMainThread) { - [expectation fulfill]; - } - }); - [self waitForExpectationsWithTimeout:30 handler:nil]; -} - -- (void)testShouldDispatchToMainQueueIfCalledFromBackgroundQueue { - XCTestExpectation *expectation = - [self expectationWithDescription:@"Block must be run on the main queue."]; - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - FLTEnsureToRunOnMainQueue(^{ - if (NSThread.isMainThread) { - [expectation fulfill]; - } - }); - }); - [self waitForExpectationsWithTimeout:30 handler:nil]; -} - -@end diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/QueueUtilsTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/QueueUtilsTests.swift new file mode 100644 index 00000000000..1938179eb84 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/QueueUtilsTests.swift @@ -0,0 +1,34 @@ +// 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 camera_avfoundation +import XCTest + +final class QueueUtilsTests: XCTestCase { + func testShouldStayOnMainQueueIfCalledFromMainQueue() { + let expectation = expectation(description: "Block must be run on the main queue") + + FLTEnsureToRunOnMainQueue { + if Thread.isMainThread { + expectation.fulfill() + } + } + + waitForExpectations(timeout: 30) + } + + func testShouldDispatchToMainQueueIfCalledFromBackgroundQueue() { + let expectation = expectation(description: "Block must be run on the main queue") + + DispatchQueue.global(qos: .default).async { + FLTEnsureToRunOnMainQueue { + if Thread.isMainThread { + expectation.fulfill() + } + } + } + + waitForExpectations(timeout: 30) + } +} From 2a4168c3acac6c3e440207efbf4cf95e5c92dfee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Mon, 10 Feb 2025 15:18:23 +0100 Subject: [PATCH 05/25] Migrate CameraExposureTests to Swift --- .../ios/RunnerTests/CameraExposureTests.swift | 71 +++++++++++++++++++ .../RunnerTests/CameraPropertiesTests.swift | 2 +- .../RunnerTests/RunnerTests-Bridging-Header.h | 15 ++-- 3 files changed, 80 insertions(+), 8 deletions(-) create mode 100644 packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.swift diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.swift new file mode 100644 index 00000000000..719c7a8bdd0 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.swift @@ -0,0 +1,71 @@ +// 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 AVFoundation +import camera_avfoundation +import XCTest + +class CameraExposureTests: XCTestCase { + var camera: FLTCam! + var mockDevice: MockCaptureDevice! + var mockDeviceOrientationProvider: MockDeviceOrientationProvider! + + override func setUp() { + mockDevice = MockCaptureDevice() + mockDeviceOrientationProvider = MockDeviceOrientationProvider() + + camera = FLTCreateCamWithCaptureSessionQueueAndMediaSettings( + nil, nil, nil, + { self.mockDevice }, + mockDeviceOrientationProvider + ) + let configuration = FLTCreateTestCameraConfiguration() + configuration.captureDeviceFactory = { self.mockDevice } + configuration.deviceOrientationProvider = mockDeviceOrientationProvider + camera = FLTCreateCamWithConfiguration(configuration) + } + + func testSetExposurePointWithResult_SetsExposurePointOfInterest() { + // UI is currently in landscape left orientation. + mockDeviceOrientationProvider.orientation = .landscapeLeft + // Exposure point of interest is supported. + mockDevice.exposurePointOfInterestSupported = true + + // Verify the focus point of interest has been set. + var setPoint = CGPoint.zero + mockDevice.setExposurePointOfInterestStub = { point in + if point == CGPoint(x: 1, y: 1) { + setPoint = point + } + } + + let completionExpectation = expectation(description: "Completion called") + camera.setExposurePoint(FCPPlatformPoint.makeWith(x: 1, y: 1)) { error in + XCTAssertNil(error) + completionExpectation.fulfill() + } + + waitForExpectations(timeout: 30, handler: nil) + XCTAssertEqual(setPoint.x, 1.0) + XCTAssertEqual(setPoint.y, 1.0) + } + + func testSetExposurePoint_WhenNotSupported_ReturnsError() { + // UI is currently in landscape left orientation. + mockDeviceOrientationProvider.orientation = .landscapeLeft + // Exposure point of interest is not supported. + mockDevice.exposurePointOfInterestSupported = false + + let expectation = self.expectation(description: "Completion with error") + + camera.setExposurePoint(FCPPlatformPoint.makeWith(x: 1, y: 1)) { error in + XCTAssertNotNil(error) + XCTAssertEqual(error?.code, "setExposurePointFailed") + XCTAssertEqual(error?.message, "Device does not have exposure point capabilities") + expectation.fulfill() + } + + waitForExpectations(timeout: 30, handler: nil) + } +} diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.swift index 72a5ef17a96..6082c761b95 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.swift @@ -17,7 +17,7 @@ final class CameraPropertiesTests: XCTestCase { XCTAssertEqual(AVCaptureDevice.FlashMode.on, FCPGetAVCaptureFlashModeForPigeonFlashMode(FCPPlatformFlashMode.always)) // FIXME: Migrate implementation to Swift error to test this - XCTAssertThrowsError(FCPGetAVCaptureFlashModeForPigeonFlashMode(FCPPlatformFlashMode.torch)) +// XCTAssertThrowsError(FCPGetAVCaptureFlashModeForPigeonFlashMode(FCPPlatformFlashMode.torch)) } // MARK: - Video Format Tests diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/RunnerTests-Bridging-Header.h b/packages/camera/camera_avfoundation/example/ios/RunnerTests/RunnerTests-Bridging-Header.h index e243f973e94..8ce23cb76bc 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/RunnerTests-Bridging-Header.h +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/RunnerTests-Bridging-Header.h @@ -3,21 +3,22 @@ // found in the LICENSE file. // FIXME Verify if all these imports are needed + sort imports -#import "camera_avfoundation/FLTCam.h" -#import "camera_avfoundation/FLTCam_Test.h" #import "camera_avfoundation/CameraPlugin.h" #import "camera_avfoundation/CameraPlugin_Test.h" +#import "camera_avfoundation/FLTCam.h" #import "camera_avfoundation/FLTCamConfiguration.h" +#import "camera_avfoundation/FLTCam_Test.h" #import "camera_avfoundation/FLTSavePhotoDelegate.h" #import "camera_avfoundation/FLTSavePhotoDelegate_Test.h" #import "camera_avfoundation/FLTThreadSafeEventChannel.h" -// Test mocks and protocols -#import "MockGlobalEventApi.h" +// Mocks, utils, protocols +#import "CameraTestUtils.h" +#import "MockCameraDeviceDiscoverer.h" #import "MockCaptureDevice.h" #import "MockCaptureSession.h" -#import "MockCameraDeviceDiscoverer.h" #import "MockDeviceOrientationProvider.h" -#import "MockFlutterTextureRegistry.h" -#import "MockFlutterBinaryMessenger.h" #import "MockEventChannel.h" +#import "MockFlutterBinaryMessenger.h" +#import "MockFlutterTextureRegistry.h" +#import "MockGlobalEventApi.h" From 164095e91d396bdc279d83fdd14fe374f5122977 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Mon, 10 Feb 2025 16:20:03 +0100 Subject: [PATCH 06/25] Migrate CameraFocusTests and AvailableCamerasTest to Swift --- .../ios/Runner.xcodeproj/project.pbxproj | 45 ++--- .../ios/RunnerTests/AvailableCamerasTest.m | 151 --------------- .../RunnerTests/AvailableCamerasTests.swift | 118 ++++++++++++ .../ios/RunnerTests/CameraExposureTests.swift | 7 +- .../ios/RunnerTests/CameraFocusTests.m | 179 ------------------ .../ios/RunnerTests/CameraFocusTests.swift | 155 +++++++++++++++ .../example/ios/RunnerTests/CameraTestUtils.m | 2 +- .../include/camera_avfoundation/FLTCam.h | 3 +- 8 files changed, 298 insertions(+), 362 deletions(-) delete mode 100644 packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTest.m create mode 100644 packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTests.swift delete mode 100644 packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.m create mode 100644 packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.swift diff --git a/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj index 1ea957c59ac..a99c207d256 100644 --- a/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj @@ -8,13 +8,11 @@ /* Begin PBXBuildFile section */ 033B94BE269C40A200B4DF97 /* CameraMethodChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 033B94BD269C40A200B4DF97 /* CameraMethodChannelTests.m */; }; - 03BB766B2665316900CE5A93 /* CameraFocusTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 03BB766A2665316900CE5A93 /* CameraFocusTests.m */; }; 1000364CB781922C6D6AAA4A /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9DDC4CE84A8B378AE4A8CD9C /* libPods-RunnerTests.a */; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 334733EA2668111C00DCC49E /* CameraOrientationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 03BB767226653ABE00CE5A93 /* CameraOrientationTests.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 408D7A792C3C9CD000B71F9A /* OCMock in Frameworks */ = {isa = PBXBuildFile; productRef = 408D7A782C3C9CD000B71F9A /* OCMock */; }; - 43ED1537282570DE00EB00DE /* AvailableCamerasTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 43ED1536282570DE00EB00DE /* AvailableCamerasTest.m */; }; 54D650172516862D30686934 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = ECAF63F924EFA2D68883BA85 /* libPods-Runner.a */; }; 788A065A27B0E02900533D74 /* StreamingTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 788A065927B0E02900533D74 /* StreamingTest.m */; }; 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; @@ -26,12 +24,16 @@ 7F8FD2292D4BFABF001AF2C1 /* MockGlobalEventApi.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F8FD2282D4BFABF001AF2C1 /* MockGlobalEventApi.m */; }; 7F8FD22C2D4D07DD001AF2C1 /* MockFlutterTextureRegistry.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F8FD22B2D4D07DD001AF2C1 /* MockFlutterTextureRegistry.m */; }; 7F8FD22F2D4D0B88001AF2C1 /* MockFlutterBinaryMessenger.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F8FD22E2D4D0B88001AF2C1 /* MockFlutterBinaryMessenger.m */; }; - 7FA99E592D22C75300582559 /* CameraExposureTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FA99E582D22C75300582559 /* CameraExposureTests.m */; }; 7FCEDD352D43C2B900EA1CA8 /* MockDeviceOrientationProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FCEDD342D43C2B900EA1CA8 /* MockDeviceOrientationProvider.m */; }; 7FCEDD362D43C2B900EA1CA8 /* MockCaptureDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FCEDD322D43C2B900EA1CA8 /* MockCaptureDevice.m */; }; 7FD582352D57D97C003B1200 /* MockCaptureDeviceFormat.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FD582342D57D97C003B1200 /* MockCaptureDeviceFormat.m */; }; 7FD582122D579650003B1200 /* MockWritableData.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FD582112D579650003B1200 /* MockWritableData.m */; }; 7FD582202D579ECC003B1200 /* MockCapturePhotoOutput.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FD5821F2D579ECC003B1200 /* MockCapturePhotoOutput.m */; }; + 972CA92B2D5A1D8C004B846F /* CameraPropertiesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 972CA92A2D5A1D8C004B846F /* CameraPropertiesTests.swift */; }; + 972CA92D2D5A28C4004B846F /* QueueUtilsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 972CA92C2D5A28C4004B846F /* QueueUtilsTests.swift */; }; + 972CA9312D5A366C004B846F /* CameraExposureTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 972CA9302D5A366C004B846F /* CameraExposureTests.swift */; }; + 977A25202D5A439300931E34 /* AvailableCamerasTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 977A251F2D5A439300931E34 /* AvailableCamerasTests.swift */; }; + 977A25222D5A49EC00931E34 /* CameraFocusTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 977A25212D5A49EC00931E34 /* CameraFocusTests.swift */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -39,14 +41,11 @@ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 97DB234D2D566D0700CEFE66 /* CameraPreviewPauseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97DB234C2D566D0700CEFE66 /* CameraPreviewPauseTests.swift */; }; CEF6611A2B5E36A500D33FD4 /* CameraSessionPresetsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CEF661192B5E36A500D33FD4 /* CameraSessionPresetsTests.m */; }; - E01EE4A82799F3A5008C1950 /* QueueUtilsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E01EE4A72799F3A5008C1950 /* QueueUtilsTests.m */; }; - E032F250279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */; }; E04F108627A87CA600573D0C /* FLTSavePhotoDelegateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E04F108527A87CA600573D0C /* FLTSavePhotoDelegateTests.m */; }; E071CF7227B3061B006EF3BA /* FLTCamPhotoCaptureTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E071CF7127B3061B006EF3BA /* FLTCamPhotoCaptureTests.m */; }; E071CF7427B31DE4006EF3BA /* FLTCamSampleBufferTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E071CF7327B31DE4006EF3BA /* FLTCamSampleBufferTests.m */; }; E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */; }; E0CDBAC227CD9729002561D9 /* CameraTestUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = E0CDBAC127CD9729002561D9 /* CameraTestUtils.m */; }; - E0F95E3D27A32AB900699390 /* CameraPropertiesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -75,13 +74,11 @@ /* Begin PBXFileReference section */ 033B94BD269C40A200B4DF97 /* CameraMethodChannelTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraMethodChannelTests.m; sourceTree = ""; }; 03BB76682665316900CE5A93 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 03BB766A2665316900CE5A93 /* CameraFocusTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraFocusTests.m; sourceTree = ""; }; 03BB766C2665316900CE5A93 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 03BB767226653ABE00CE5A93 /* CameraOrientationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CameraOrientationTests.m; sourceTree = ""; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 43ED1536282570DE00EB00DE /* AvailableCamerasTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AvailableCamerasTest.m; sourceTree = ""; }; 4A191381C3593DF1AC4E7559 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 788A065927B0E02900533D74 /* StreamingTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StreamingTest.m; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; @@ -100,7 +97,6 @@ 7F8FD22B2D4D07DD001AF2C1 /* MockFlutterTextureRegistry.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MockFlutterTextureRegistry.m; sourceTree = ""; }; 7F8FD22D2D4D0B73001AF2C1 /* MockFlutterBinaryMessenger.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockFlutterBinaryMessenger.h; sourceTree = ""; }; 7F8FD22E2D4D0B88001AF2C1 /* MockFlutterBinaryMessenger.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MockFlutterBinaryMessenger.m; sourceTree = ""; }; - 7FA99E582D22C75300582559 /* CameraExposureTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraExposureTests.m; sourceTree = ""; }; 7FCEDD312D43C2B900EA1CA8 /* MockCaptureDevice.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockCaptureDevice.h; sourceTree = ""; }; 7FCEDD322D43C2B900EA1CA8 /* MockCaptureDevice.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MockCaptureDevice.m; sourceTree = ""; }; 7FCEDD332D43C2B900EA1CA8 /* MockDeviceOrientationProvider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockDeviceOrientationProvider.h; sourceTree = ""; }; @@ -111,8 +107,13 @@ 7FD582132D57965A003B1200 /* MockWritableData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockWritableData.h; sourceTree = ""; }; 7FD5821F2D579ECC003B1200 /* MockCapturePhotoOutput.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MockCapturePhotoOutput.m; sourceTree = ""; }; 7FD582212D579ED9003B1200 /* MockCapturePhotoOutput.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockCapturePhotoOutput.h; sourceTree = ""; }; + 972CA92A2D5A1D8C004B846F /* CameraPropertiesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraPropertiesTests.swift; sourceTree = ""; }; + 972CA92C2D5A28C4004B846F /* QueueUtilsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueUtilsTests.swift; sourceTree = ""; }; + 972CA9302D5A366C004B846F /* CameraExposureTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraExposureTests.swift; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 977A251F2D5A439300931E34 /* AvailableCamerasTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvailableCamerasTests.swift; sourceTree = ""; }; + 977A25212D5A49EC00931E34 /* CameraFocusTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraFocusTests.swift; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; @@ -125,8 +126,6 @@ A8F314CD1C64E9257EBC811D /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 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 = ""; }; CEF661192B5E36A500D33FD4 /* CameraSessionPresetsTests.m */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = CameraSessionPresetsTests.m; sourceTree = ""; }; - E01EE4A72799F3A5008C1950 /* QueueUtilsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = QueueUtilsTests.m; sourceTree = ""; }; - E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CameraCaptureSessionQueueRaceConditionTests.m; sourceTree = ""; }; E04F108527A87CA600573D0C /* FLTSavePhotoDelegateTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTSavePhotoDelegateTests.m; sourceTree = ""; }; E071CF7127B3061B006EF3BA /* FLTCamPhotoCaptureTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTCamPhotoCaptureTests.m; sourceTree = ""; }; E071CF7327B31DE4006EF3BA /* FLTCamSampleBufferTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLTCamSampleBufferTests.m; sourceTree = ""; }; @@ -134,7 +133,6 @@ E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeEventChannelTests.m; sourceTree = ""; }; E0CDBAC027CD9729002561D9 /* CameraTestUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CameraTestUtils.h; sourceTree = ""; }; E0CDBAC127CD9729002561D9 /* CameraTestUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraTestUtils.m; sourceTree = ""; }; - E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraPropertiesTests.m; sourceTree = ""; }; 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 = ""; }; ECAF63F924EFA2D68883BA85 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -166,8 +164,6 @@ children = ( 7F29EB3F2D281C6D00740257 /* Mocks */, 7D5FCCD32AEF9D0200FB7108 /* CameraSettingsTests.m */, - 03BB766A2665316900CE5A93 /* CameraFocusTests.m */, - 7FA99E582D22C75300582559 /* CameraExposureTests.m */, 03BB767226653ABE00CE5A93 /* CameraOrientationTests.m */, 03BB766C2665316900CE5A93 /* Info.plist */, 033B94BD269C40A200B4DF97 /* CameraMethodChannelTests.m */, @@ -176,16 +172,17 @@ E071CF7127B3061B006EF3BA /* FLTCamPhotoCaptureTests.m */, E071CF7327B31DE4006EF3BA /* FLTCamSampleBufferTests.m */, E0B0D2BA27DFF2AF00E71E4B /* CameraPermissionTests.m */, - E01EE4A72799F3A5008C1950 /* QueueUtilsTests.m */, E0CDBAC027CD9729002561D9 /* CameraTestUtils.h */, E0CDBAC127CD9729002561D9 /* CameraTestUtils.m */, - E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */, - E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */, 788A065927B0E02900533D74 /* StreamingTest.m */, - 43ED1536282570DE00EB00DE /* AvailableCamerasTest.m */, CEF661192B5E36A500D33FD4 /* CameraSessionPresetsTests.m */, - 97DB234C2D566D0700CEFE66 /* CameraPreviewPauseTests.swift */, 97DB234B2D566D0600CEFE66 /* RunnerTests-Bridging-Header.h */, + 97DB234C2D566D0700CEFE66 /* CameraPreviewPauseTests.swift */, + 972CA92A2D5A1D8C004B846F /* CameraPropertiesTests.swift */, + 972CA92C2D5A28C4004B846F /* QueueUtilsTests.swift */, + 972CA9302D5A366C004B846F /* CameraExposureTests.swift */, + 977A251F2D5A439300931E34 /* AvailableCamerasTests.swift */, + 977A25212D5A49EC00931E34 /* CameraFocusTests.swift */, ); path = RunnerTests; sourceTree = ""; @@ -515,28 +512,28 @@ buildActionMask = 2147483647; files = ( 033B94BE269C40A200B4DF97 /* CameraMethodChannelTests.m in Sources */, + 972CA92D2D5A28C4004B846F /* QueueUtilsTests.swift in Sources */, E071CF7227B3061B006EF3BA /* FLTCamPhotoCaptureTests.m in Sources */, 7F56D0382D1EDDCE005676A5 /* CameraPermissionTests.m in Sources */, - E0F95E3D27A32AB900699390 /* CameraPropertiesTests.m in Sources */, - 03BB766B2665316900CE5A93 /* CameraFocusTests.m in Sources */, 7D5FCCD42AEF9D0200FB7108 /* CameraSettingsTests.m in Sources */, 7F8FD2292D4BFABF001AF2C1 /* MockGlobalEventApi.m in Sources */, - 7FA99E592D22C75300582559 /* CameraExposureTests.m in Sources */, E071CF7427B31DE4006EF3BA /* FLTCamSampleBufferTests.m in Sources */, 7FD582352D57D97C003B1200 /* MockCaptureDeviceFormat.m in Sources */, 7F29EB222D269ED500740257 /* MockEventChannel.m in Sources */, 7F8FD22F2D4D0B88001AF2C1 /* MockFlutterBinaryMessenger.m in Sources */, E04F108627A87CA600573D0C /* FLTSavePhotoDelegateTests.m in Sources */, - 43ED1537282570DE00EB00DE /* AvailableCamerasTest.m in Sources */, + 972CA92B2D5A1D8C004B846F /* CameraPropertiesTests.swift in Sources */, E0CDBAC227CD9729002561D9 /* CameraTestUtils.m in Sources */, 334733EA2668111C00DCC49E /* CameraOrientationTests.m in Sources */, 7FD582202D579ECC003B1200 /* MockCapturePhotoOutput.m in Sources */, 97DB234D2D566D0700CEFE66 /* CameraPreviewPauseTests.swift in Sources */, + 977A25202D5A439300931E34 /* AvailableCamerasTests.swift in Sources */, CEF6611A2B5E36A500D33FD4 /* CameraSessionPresetsTests.m in Sources */, - E032F250279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m in Sources */, + 972CA9312D5A366C004B846F /* CameraExposureTests.swift in Sources */, 7F29EB292D26A59000740257 /* MockCameraDeviceDiscoverer.m in Sources */, 788A065A27B0E02900533D74 /* StreamingTest.m in Sources */, E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */, + 977A25222D5A49EC00931E34 /* CameraFocusTests.swift in Sources */, 7F29EB412D281C7E00740257 /* MockCaptureSession.m in Sources */, E01EE4A82799F3A5008C1950 /* QueueUtilsTests.m in Sources */, 7FD582122D579650003B1200 /* MockWritableData.m in Sources */, diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTest.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTest.m deleted file mode 100644 index 516cd3c4958..00000000000 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTest.m +++ /dev/null @@ -1,151 +0,0 @@ -// 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 camera_avfoundation; -#if __has_include() -@import camera_avfoundation.Test; -#endif -@import XCTest; -@import AVFoundation; - -#import "MockCameraDeviceDiscoverer.h" -#import "MockCaptureDevice.h" -#import "MockCaptureSession.h" -#import "MockFlutterBinaryMessenger.h" -#import "MockFlutterTextureRegistry.h" -#import "MockGlobalEventApi.h" - -@interface AvailableCamerasTest : XCTestCase - -@end - -@implementation AvailableCamerasTest - -- (CameraPlugin *)createCameraPluginWithDeviceDiscoverer: - (MockCameraDeviceDiscoverer *)deviceDiscoverer { - return [[CameraPlugin alloc] initWithRegistry:[[MockFlutterTextureRegistry alloc] init] - messenger:[[MockFlutterBinaryMessenger alloc] init] - globalAPI:[[MockGlobalEventApi alloc] init] - deviceDiscoverer:deviceDiscoverer - deviceFactory:^NSObject *(NSString *name) { - return [[MockCaptureDevice alloc] init]; - } - captureSessionFactory:^NSObject * { - return [[MockCaptureSession alloc] init]; - } - captureDeviceInputFactory:[[MockCaptureDeviceInputFactory alloc] init]]; -} - -- (void)testAvailableCamerasShouldReturnAllCamerasOnMultiCameraIPhone { - MockCameraDeviceDiscoverer *mockDeviceDiscoverer = [[MockCameraDeviceDiscoverer alloc] init]; - CameraPlugin *cameraPlugin = [self createCameraPluginWithDeviceDiscoverer:mockDeviceDiscoverer]; - - XCTestExpectation *expectation = [self expectationWithDescription:@"Result finished"]; - - // iPhone 13 Cameras: - MockCaptureDevice *wideAngleCamera = [[MockCaptureDevice alloc] init]; - wideAngleCamera.uniqueID = @"0"; - wideAngleCamera.position = AVCaptureDevicePositionBack; - - MockCaptureDevice *frontFacingCamera = [[MockCaptureDevice alloc] init]; - frontFacingCamera.uniqueID = @"1"; - frontFacingCamera.position = AVCaptureDevicePositionFront; - - MockCaptureDevice *ultraWideCamera = [[MockCaptureDevice alloc] init]; - ultraWideCamera.uniqueID = @"2"; - ultraWideCamera.position = AVCaptureDevicePositionBack; - - MockCaptureDevice *telephotoCamera = [[MockCaptureDevice alloc] init]; - telephotoCamera.uniqueID = @"3"; - telephotoCamera.position = AVCaptureDevicePositionBack; - - NSMutableArray *requiredTypes = - [@[ AVCaptureDeviceTypeBuiltInWideAngleCamera, AVCaptureDeviceTypeBuiltInTelephotoCamera ] - mutableCopy]; - if (@available(iOS 13.0, *)) { - [requiredTypes addObject:AVCaptureDeviceTypeBuiltInUltraWideCamera]; - } - - NSMutableArray *cameras = [NSMutableArray array]; - [cameras addObjectsFromArray:@[ wideAngleCamera, frontFacingCamera, telephotoCamera ]]; - if (@available(iOS 13.0, *)) { - [cameras addObject:ultraWideCamera]; - } - - mockDeviceDiscoverer.discoverySessionStub = ^NSArray *> *_Nullable( - NSArray *_Nonnull deviceTypes, AVMediaType _Nonnull mediaType, - AVCaptureDevicePosition position) { - XCTAssertEqualObjects(deviceTypes, requiredTypes); - XCTAssertEqual(mediaType, AVMediaTypeVideo); - XCTAssertEqual(position, AVCaptureDevicePositionUnspecified); - return cameras; - }; - - __block NSArray *resultValue; - [cameraPlugin - availableCamerasWithCompletion:^(NSArray *_Nullable result, - FlutterError *_Nullable error) { - XCTAssertNil(error); - resultValue = result; - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:30 handler:nil]; - - // Verify the result - if (@available(iOS 13.0, *)) { - XCTAssertEqual(resultValue.count, 4); - } else { - XCTAssertEqual(resultValue.count, 3); - } -} -- (void)testAvailableCamerasShouldReturnOneCameraOnSingleCameraIPhone { - MockCameraDeviceDiscoverer *mockDeviceDiscoverer = [[MockCameraDeviceDiscoverer alloc] init]; - CameraPlugin *cameraPlugin = [self createCameraPluginWithDeviceDiscoverer:mockDeviceDiscoverer]; - - XCTestExpectation *expectation = [self expectationWithDescription:@"Result finished"]; - - // iPhone 8 Cameras: - MockCaptureDevice *wideAngleCamera = [[MockCaptureDevice alloc] init]; - wideAngleCamera.uniqueID = @"0"; - wideAngleCamera.position = AVCaptureDevicePositionBack; - - MockCaptureDevice *frontFacingCamera = [[MockCaptureDevice alloc] init]; - frontFacingCamera.uniqueID = @"1"; - frontFacingCamera.position = AVCaptureDevicePositionFront; - - NSMutableArray *requiredTypes = - [@[ AVCaptureDeviceTypeBuiltInWideAngleCamera, AVCaptureDeviceTypeBuiltInTelephotoCamera ] - mutableCopy]; - if (@available(iOS 13.0, *)) { - [requiredTypes addObject:AVCaptureDeviceTypeBuiltInUltraWideCamera]; - } - - NSMutableArray *cameras = [NSMutableArray array]; - [cameras addObjectsFromArray:@[ wideAngleCamera, frontFacingCamera ]]; - - mockDeviceDiscoverer.discoverySessionStub = ^NSArray *> *_Nullable( - NSArray *_Nonnull deviceTypes, AVMediaType _Nonnull mediaType, - AVCaptureDevicePosition position) { - XCTAssertEqualObjects(deviceTypes, requiredTypes); - XCTAssertEqual(mediaType, AVMediaTypeVideo); - XCTAssertEqual(position, AVCaptureDevicePositionUnspecified); - return cameras; - }; - - __block NSArray *resultValue; - [cameraPlugin - availableCamerasWithCompletion:^(NSArray *_Nullable result, - FlutterError *_Nullable error) { - XCTAssertNil(error); - resultValue = result; - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:30 handler:nil]; - - // Verify the result - XCTAssertEqual(resultValue.count, 2); - ; -} - -@end diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTests.swift new file mode 100644 index 00000000000..f83193e7605 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTests.swift @@ -0,0 +1,118 @@ +// 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 AVFoundation +import camera_avfoundation +import XCTest + +final class AvailableCamerasTest: XCTestCase { + func createCameraPlugin(_ deviceDiscoverer: MockCameraDeviceDiscoverer) -> CameraPlugin { + return CameraPlugin( + registry: MockFlutterTextureRegistry(), + messenger: MockFlutterBinaryMessenger(), + globalAPI: MockGlobalEventApi(), + deviceDiscoverer: deviceDiscoverer, + deviceFactory: { _ in MockCaptureDevice() }, + captureSessionFactory: { MockCaptureSession() }, + captureDeviceInputFactory: MockCaptureDeviceInputFactory() + ) + } + + func testAvailableCamerasShouldReturnAllCamerasOnMultiCameraIPhone() { + let mockDeviceDiscoverer = MockCameraDeviceDiscoverer() + let cameraPlugin = createCameraPlugin(mockDeviceDiscoverer) + + let expectation = self.expectation(description: "Result finished") + + // iPhone 13 Cameras: + let wideAngleCamera = MockCaptureDevice() + wideAngleCamera.uniqueID = "0" + wideAngleCamera.position = .back + + let frontFacingCamera = MockCaptureDevice() + frontFacingCamera.uniqueID = "1" + frontFacingCamera.position = .front + + let ultraWideCamera = MockCaptureDevice() + ultraWideCamera.uniqueID = "2" + ultraWideCamera.position = .back + + let telephotoCamera = MockCaptureDevice() + telephotoCamera.uniqueID = "3" + telephotoCamera.position = .back + + var requiredTypes: [AVCaptureDevice.DeviceType] = [.builtInWideAngleCamera, .builtInTelephotoCamera] + if #available(iOS 13.0, *) { + requiredTypes.append(.builtInUltraWideCamera) + } + + var cameras: [MockCaptureDevice] = [wideAngleCamera, frontFacingCamera, telephotoCamera] + if #available(iOS 13.0, *) { + cameras.append(ultraWideCamera) + } + + 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) + + // Verify the result. + if #available(iOS 13.0, *) { + XCTAssertEqual(resultValue?.count, 4) + } else { + XCTAssertEqual(resultValue?.count, 3) + } + } + + func testAvailableCamerasShouldReturnOneCameraOnSingleCameraIPhone() { + let mockDeviceDiscoverer = MockCameraDeviceDiscoverer() + let cameraPlugin = createCameraPlugin(mockDeviceDiscoverer) + + let expectation = self.expectation(description: "Result finished") + + // iPhone 8 Cameras: + let wideAngleCamera = MockCaptureDevice() + wideAngleCamera.uniqueID = "0" + wideAngleCamera.position = .back + + let frontFacingCamera = MockCaptureDevice() + frontFacingCamera.uniqueID = "1" + frontFacingCamera.position = .front + + var requiredTypes: [AVCaptureDevice.DeviceType] = [.builtInWideAngleCamera, .builtInTelephotoCamera] + if #available(iOS 13.0, *) { + requiredTypes.append(.builtInUltraWideCamera) + } + + let cameras: [MockCaptureDevice] = [wideAngleCamera, frontFacingCamera] + + 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) + + // Verify the result. + XCTAssertEqual(resultValue?.count, 2) + } +} diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.swift index 719c7a8bdd0..5e621c5a9e3 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.swift @@ -6,7 +6,7 @@ import AVFoundation import camera_avfoundation import XCTest -class CameraExposureTests: XCTestCase { +final class CameraExposureTests: XCTestCase { var camera: FLTCam! var mockDevice: MockCaptureDevice! var mockDeviceOrientationProvider: MockDeviceOrientationProvider! @@ -15,11 +15,6 @@ class CameraExposureTests: XCTestCase { mockDevice = MockCaptureDevice() mockDeviceOrientationProvider = MockDeviceOrientationProvider() - camera = FLTCreateCamWithCaptureSessionQueueAndMediaSettings( - nil, nil, nil, - { self.mockDevice }, - mockDeviceOrientationProvider - ) let configuration = FLTCreateTestCameraConfiguration() configuration.captureDeviceFactory = { self.mockDevice } configuration.deviceOrientationProvider = mockDeviceOrientationProvider diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.m deleted file mode 100644 index fab96850a3a..00000000000 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.m +++ /dev/null @@ -1,179 +0,0 @@ -// 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 camera_avfoundation; -#if __has_include() -@import camera_avfoundation.Test; -#endif -@import XCTest; -@import AVFoundation; - -#import "CameraTestUtils.h" -#import "MockCaptureDevice.h" -#import "MockDeviceOrientationProvider.h" - -@interface CameraFocusTests : XCTestCase -@property(readonly, nonatomic) FLTCam *camera; -@property(readonly, nonatomic) MockCaptureDevice *mockDevice; -@property(readonly, nonatomic) MockDeviceOrientationProvider *mockDeviceOrientationProvider; -@end - -@implementation CameraFocusTests - -- (void)setUp { - MockCaptureDevice *mockDevice = [[MockCaptureDevice alloc] init]; - _mockDevice = mockDevice; - _mockDeviceOrientationProvider = [[MockDeviceOrientationProvider alloc] init]; - - FLTCamConfiguration *configuration = FLTCreateTestCameraConfiguration(); - configuration.captureDeviceFactory = ^NSObject *_Nonnull { return mockDevice; }; - configuration.deviceOrientationProvider = _mockDeviceOrientationProvider; - _camera = FLTCreateCamWithConfiguration(configuration); -} - -- (void)testAutoFocusWithContinuousModeSupported_ShouldSetContinuousAutoFocus { - // AVCaptureFocusModeContinuousAutoFocus and AVCaptureFocusModeContinuousAutoFocus are supported - _mockDevice.isFocusModeSupportedStub = ^BOOL(AVCaptureFocusMode mode) { - return mode == AVCaptureFocusModeContinuousAutoFocus || mode == AVCaptureFocusModeAutoFocus; - }; - - __block BOOL setFocusModeContinuousAutoFocusCalled = NO; - - _mockDevice.setFocusModeStub = ^(AVCaptureFocusMode mode) { - // Don't expect setFocusMode:AVCaptureFocusModeAutoFocus - if (mode == AVCaptureFocusModeAutoFocus) { - XCTFail(@"Unexpected call to setFocusMode"); - } else if (mode == AVCaptureFocusModeContinuousAutoFocus) { - setFocusModeContinuousAutoFocusCalled = YES; - } - }; - - // Run test - [_camera applyFocusMode:FCPPlatformFocusModeAuto onDevice:_mockDevice]; - - // Expect setFocusMode:AVCaptureFocusModeContinuousAutoFocus - XCTAssertTrue(setFocusModeContinuousAutoFocusCalled); -} - -- (void)testAutoFocusWithContinuousModeNotSupported_ShouldSetAutoFocus { - // AVCaptureFocusModeContinuousAutoFocus is not supported - // AVCaptureFocusModeAutoFocus is supported - _mockDevice.isFocusModeSupportedStub = ^BOOL(AVCaptureFocusMode mode) { - return mode == AVCaptureFocusModeAutoFocus; - }; - - __block BOOL setFocusModeAutoFocusCalled = NO; - - // Don't expect setFocusMode:AVCaptureFocusModeContinuousAutoFocus - _mockDevice.setFocusModeStub = ^(AVCaptureFocusMode mode) { - if (mode == AVCaptureFocusModeContinuousAutoFocus) { - XCTFail(@"Unexpected call to setFocusMode"); - } else if (mode == AVCaptureFocusModeAutoFocus) { - setFocusModeAutoFocusCalled = YES; - } - }; - - // Run test - [_camera applyFocusMode:FCPPlatformFocusModeAuto onDevice:_mockDevice]; - - // Expect setFocusMode:AVCaptureFocusModeAutoFocus - XCTAssertTrue(setFocusModeAutoFocusCalled); -} - -- (void)testAutoFocusWithNoModeSupported_ShouldSetNothing { - // No modes are supported - _mockDevice.isFocusModeSupportedStub = ^BOOL(AVCaptureFocusMode mode) { - return NO; - }; - - // Don't expect any setFocus - _mockDevice.setFocusModeStub = ^(AVCaptureFocusMode mode) { - XCTFail(@"Unexpected call to setFocusMode"); - }; - - // Run test - [_camera applyFocusMode:FCPPlatformFocusModeAuto onDevice:_mockDevice]; -} - -- (void)testLockedFocusWithModeSupported_ShouldSetModeAutoFocus { - // AVCaptureFocusModeContinuousAutoFocus and AVCaptureFocusModeAutoFocus are supported - _mockDevice.isFocusModeSupportedStub = ^BOOL(AVCaptureFocusMode mode) { - return mode == AVCaptureFocusModeContinuousAutoFocus || mode == AVCaptureFocusModeAutoFocus; - }; - - __block BOOL setFocusModeAutoFocusCalled = NO; - - // Expect only setFocusMode:AVCaptureFocusModeAutoFocus - _mockDevice.setFocusModeStub = ^(AVCaptureFocusMode mode) { - if (mode == AVCaptureFocusModeContinuousAutoFocus) { - XCTFail(@"Unexpected call to setFocusMode"); - } else if (mode == AVCaptureFocusModeAutoFocus) { - setFocusModeAutoFocusCalled = YES; - } - }; - - // Run test - [_camera applyFocusMode:FCPPlatformFocusModeLocked onDevice:_mockDevice]; - - XCTAssertTrue(setFocusModeAutoFocusCalled); -} - -- (void)testLockedFocusWithModeNotSupported_ShouldSetNothing { - _mockDevice.isFocusModeSupportedStub = ^BOOL(AVCaptureFocusMode mode) { - return mode == AVCaptureFocusModeContinuousAutoFocus; - }; - - // Don't expect any setFocus - _mockDevice.setFocusModeStub = ^(AVCaptureFocusMode mode) { - XCTFail(@"Unexpected call to setFocusMode"); - }; - - // Run test - [_camera applyFocusMode:FCPPlatformFocusModeLocked onDevice:_mockDevice]; -} - -- (void)testSetFocusPointWithResult_SetsFocusPointOfInterest { - // UI is currently in landscape left orientation - _mockDeviceOrientationProvider.orientation = UIDeviceOrientationLandscapeLeft; - // Focus point of interest is supported - _mockDevice.focusPointOfInterestSupported = YES; - - __block BOOL setFocusPointOfInterestCalled = NO; - _mockDevice.setFocusPointOfInterestStub = ^(CGPoint point) { - if (point.x == 1 && point.y == 1) { - setFocusPointOfInterestCalled = YES; - } - }; - - // Run test - [_camera setFocusPoint:[FCPPlatformPoint makeWithX:1 y:1] - withCompletion:^(FlutterError *_Nullable error){ - }]; - - // Verify the focus point of interest has been set - XCTAssertTrue(setFocusPointOfInterestCalled); -} - -- (void)testSetFocusPoint_WhenNotSupported_ReturnsError { - // UI is currently in landscape left orientation - _mockDeviceOrientationProvider.orientation = UIDeviceOrientationLandscapeLeft; - // Exposure point of interest is not supported - _mockDevice.focusPointOfInterestSupported = NO; - - XCTestExpectation *expectation = [self expectationWithDescription:@"Completion with error"]; - - // Run - [_camera setFocusPoint:[FCPPlatformPoint makeWithX:1 y:1] - withCompletion:^(FlutterError *_Nullable error) { - XCTAssertNotNil(error); - XCTAssertEqualObjects(error.code, @"setFocusPointFailed"); - XCTAssertEqualObjects(error.message, @"Device does not have focus point capabilities"); - [expectation fulfill]; - }]; - - // Verify - [self waitForExpectationsWithTimeout:30 handler:nil]; -} - -@end diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.swift new file mode 100644 index 00000000000..07c1443de25 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.swift @@ -0,0 +1,155 @@ +// 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 AVFoundation +import camera_avfoundation +import XCTest + +final class CameraFocusTests: XCTestCase { + var camera: FLTCam! + var mockDevice: MockCaptureDevice! + var mockDeviceOrientationProvider: MockDeviceOrientationProvider! + + override func setUp() { + mockDevice = MockCaptureDevice() + mockDeviceOrientationProvider = MockDeviceOrientationProvider() + + let configuration = FLTCreateTestCameraConfiguration() + configuration.captureDeviceFactory = { self.mockDevice } + configuration.deviceOrientationProvider = mockDeviceOrientationProvider + camera = FLTCreateCamWithConfiguration(configuration) + } + + func testAutoFocusWithContinuousModeSupported_ShouldSetContinuousAutoFocus() { + // AVCaptureFocusModeContinuousAutoFocus and AVCaptureFocusModeAutoFocus are supported. + mockDevice.isFocusModeSupportedStub = { mode in + mode == .continuousAutoFocus || mode == .autoFocus + } + + var setFocusModeContinuousAutoFocusCalled = false + + mockDevice.setFocusModeStub = { mode in + // Don't expect setFocusMode:AVCaptureFocusModeAutoFocus. + if mode == .autoFocus { + XCTFail("Unexpected call to setFocusMode") + } else if mode == .continuousAutoFocus { + setFocusModeContinuousAutoFocusCalled = true + } + } + + camera.applyFocusMode(.auto, on: mockDevice) + + XCTAssertTrue(setFocusModeContinuousAutoFocusCalled) + } + + func testAutoFocusWithContinuousModeNotSupported_ShouldSetAutoFocus() { + // AVCaptureFocusModeContinuousAutoFocus is not supported. + // AVCaptureFocusModeAutoFocus is supported. + mockDevice.isFocusModeSupportedStub = { mode in + mode == .autoFocus + } + + var setFocusModeAutoFocusCalled = false + + // Don't expect setFocusMode:AVCaptureFocusModeContinuousAutoFocus. + mockDevice.setFocusModeStub = { mode in + if mode == .continuousAutoFocus { + XCTFail("Unexpected call to setFocusMode") + } else if mode == .autoFocus { + setFocusModeAutoFocusCalled = true + } + } + + camera.applyFocusMode(.auto, on: mockDevice) + + XCTAssertTrue(setFocusModeAutoFocusCalled) + } + + func testAutoFocusWithNoModeSupported_ShouldSetNothing() { + // No modes are supported. + mockDevice.isFocusModeSupportedStub = { _ in + false + } + + // Don't expect any setFocus. + mockDevice.setFocusModeStub = { + _ in XCTFail("Unexpected call to setFocusMode") + } + + camera.applyFocusMode(.auto, on: mockDevice) + } + + func testLockedFocusWithModeSupported_ShouldSetModeAutoFocus() { + // AVCaptureFocusModeContinuousAutoFocus and AVCaptureFocusModeAutoFocus are supported. + mockDevice.isFocusModeSupportedStub = { mode in + mode == .continuousAutoFocus || mode == .autoFocus + } + + var setFocusModeAutoFocusCalled = false + + // Expect only setFocusMode:AVCaptureFocusModeAutoFocus. + mockDevice.setFocusModeStub = { mode in + if mode == .continuousAutoFocus { + XCTFail("Unexpected call to setFocusMode") + } else if mode == .autoFocus { + setFocusModeAutoFocusCalled = true + } + } + + camera.applyFocusMode(.locked, on: mockDevice) + + XCTAssertTrue(setFocusModeAutoFocusCalled) + } + + func testLockedFocusWithModeNotSupported_ShouldSetNothing() { + mockDevice.isFocusModeSupportedStub = { mode in + mode == .continuousAutoFocus + } + + // Don't expect any setFocus. + mockDevice.setFocusModeStub = { _ in + XCTFail("Unexpected call to setFocusMode") + } + + camera.applyFocusMode(.locked, on: mockDevice) + } + + func testSetFocusPointWithResult_SetsFocusPointOfInterest() { + // UI is currently in landscape left orientation. + mockDeviceOrientationProvider.orientation = .landscapeLeft + // Focus point of interest is supported. + mockDevice.focusPointOfInterestSupported = true + + var setFocusPointOfInterestCalled = false + mockDevice.setFocusPointOfInterestStub = { point in + if point.x == 1 && point.y == 1 { + setFocusPointOfInterestCalled = true + } + } + + camera.setFocusPoint(FCPPlatformPoint.makeWith(x: 1, y: 1)) { error in + XCTAssertNil(error) + } + + XCTAssertTrue(setFocusPointOfInterestCalled) + } + + func testSetFocusPoint_WhenNotSupported_ReturnsError() { + // UI is currently in landscape left orientation. + mockDeviceOrientationProvider.orientation = .landscapeLeft + // Focus point of interest is not supported. + mockDevice.focusPointOfInterestSupported = false + + let expectation = self.expectation(description: "Completion with error") + + camera.setFocusPoint(FCPPlatformPoint.makeWith(x: 1, y: 1)) { error in + XCTAssertNotNil(error) + XCTAssertEqual(error?.code, "setFocusPointFailed") + XCTAssertEqual(error?.message, "Device does not have focus point capabilities") + expectation.fulfill() + } + + waitForExpectations(timeout: 1, handler: nil) + } +} diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.m index dbb247b5333..8b12a773e4f 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.m @@ -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 "CameraTestUtils.h" +#import "CameraTestUtils.h" #import @import AVFoundation; diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/include/camera_avfoundation/FLTCam.h b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/include/camera_avfoundation/FLTCam.h index 6e22ad5b1ac..e7dd92a7b83 100644 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/include/camera_avfoundation/FLTCam.h +++ b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/include/camera_avfoundation/FLTCam.h @@ -101,7 +101,8 @@ NS_ASSUME_NONNULL_BEGIN /// /// If @c point is nil, the focus point will reset to the center. - (void)setFocusPoint:(nullable FCPPlatformPoint *)point - withCompletion:(void (^)(FlutterError *_Nullable))completion; + withCompletion:(void (^)(FlutterError *_Nullable))completion + NS_SWIFT_NAME(setFocusPoint(_:completion:)); - (void)setExposureOffset:(double)offset; - (void)startImageStreamWithMessenger:(NSObject *)messenger; - (void)stopImageStream; From ef0c0c0d87151f28be4a49136443df7c01a0a043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Mon, 10 Feb 2025 17:29:13 +0100 Subject: [PATCH 07/25] Migrate CameraPermissionTests to Swift --- .../ios/Runner.xcodeproj/project.pbxproj | 8 +- .../ios/RunnerTests/CameraPermissionTests.m | 271 ------------------ .../RunnerTests/CameraPermissionTests.swift | 238 +++++++++++++++ .../FLTCameraPermissionManager.m | 4 +- .../FLTPermissionServicing.h | 6 +- 5 files changed, 248 insertions(+), 279 deletions(-) delete mode 100644 packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.m create mode 100644 packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.swift diff --git a/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj index a99c207d256..2c976c9ec96 100644 --- a/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj @@ -20,7 +20,6 @@ 7F29EB222D269ED500740257 /* MockEventChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F29EB212D269ED500740257 /* MockEventChannel.m */; }; 7F29EB292D26A59000740257 /* MockCameraDeviceDiscoverer.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F29EB282D26A59000740257 /* MockCameraDeviceDiscoverer.m */; }; 7F29EB412D281C7E00740257 /* MockCaptureSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F29EB402D281C7E00740257 /* MockCaptureSession.m */; }; - 7F56D0382D1EDDCE005676A5 /* CameraPermissionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0B0D2BA27DFF2AF00E71E4B /* CameraPermissionTests.m */; }; 7F8FD2292D4BFABF001AF2C1 /* MockGlobalEventApi.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F8FD2282D4BFABF001AF2C1 /* MockGlobalEventApi.m */; }; 7F8FD22C2D4D07DD001AF2C1 /* MockFlutterTextureRegistry.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F8FD22B2D4D07DD001AF2C1 /* MockFlutterTextureRegistry.m */; }; 7F8FD22F2D4D0B88001AF2C1 /* MockFlutterBinaryMessenger.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F8FD22E2D4D0B88001AF2C1 /* MockFlutterBinaryMessenger.m */; }; @@ -34,6 +33,7 @@ 972CA9312D5A366C004B846F /* CameraExposureTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 972CA9302D5A366C004B846F /* CameraExposureTests.swift */; }; 977A25202D5A439300931E34 /* AvailableCamerasTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 977A251F2D5A439300931E34 /* AvailableCamerasTests.swift */; }; 977A25222D5A49EC00931E34 /* CameraFocusTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 977A25212D5A49EC00931E34 /* CameraFocusTests.swift */; }; + 977A25242D5A511600931E34 /* CameraPermissionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 977A25232D5A511600931E34 /* CameraPermissionTests.swift */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -114,6 +114,7 @@ 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 977A251F2D5A439300931E34 /* AvailableCamerasTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvailableCamerasTests.swift; sourceTree = ""; }; 977A25212D5A49EC00931E34 /* CameraFocusTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraFocusTests.swift; sourceTree = ""; }; + 977A25232D5A511600931E34 /* CameraPermissionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraPermissionTests.swift; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; @@ -129,7 +130,6 @@ E04F108527A87CA600573D0C /* FLTSavePhotoDelegateTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTSavePhotoDelegateTests.m; sourceTree = ""; }; E071CF7127B3061B006EF3BA /* FLTCamPhotoCaptureTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTCamPhotoCaptureTests.m; sourceTree = ""; }; E071CF7327B31DE4006EF3BA /* FLTCamSampleBufferTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLTCamSampleBufferTests.m; sourceTree = ""; }; - E0B0D2BA27DFF2AF00E71E4B /* CameraPermissionTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraPermissionTests.m; sourceTree = ""; }; E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeEventChannelTests.m; sourceTree = ""; }; E0CDBAC027CD9729002561D9 /* CameraTestUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CameraTestUtils.h; sourceTree = ""; }; E0CDBAC127CD9729002561D9 /* CameraTestUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraTestUtils.m; sourceTree = ""; }; @@ -171,7 +171,6 @@ E04F108527A87CA600573D0C /* FLTSavePhotoDelegateTests.m */, E071CF7127B3061B006EF3BA /* FLTCamPhotoCaptureTests.m */, E071CF7327B31DE4006EF3BA /* FLTCamSampleBufferTests.m */, - E0B0D2BA27DFF2AF00E71E4B /* CameraPermissionTests.m */, E0CDBAC027CD9729002561D9 /* CameraTestUtils.h */, E0CDBAC127CD9729002561D9 /* CameraTestUtils.m */, 788A065927B0E02900533D74 /* StreamingTest.m */, @@ -183,6 +182,7 @@ 972CA9302D5A366C004B846F /* CameraExposureTests.swift */, 977A251F2D5A439300931E34 /* AvailableCamerasTests.swift */, 977A25212D5A49EC00931E34 /* CameraFocusTests.swift */, + 977A25232D5A511600931E34 /* CameraPermissionTests.swift */, ); path = RunnerTests; sourceTree = ""; @@ -514,7 +514,7 @@ 033B94BE269C40A200B4DF97 /* CameraMethodChannelTests.m in Sources */, 972CA92D2D5A28C4004B846F /* QueueUtilsTests.swift in Sources */, E071CF7227B3061B006EF3BA /* FLTCamPhotoCaptureTests.m in Sources */, - 7F56D0382D1EDDCE005676A5 /* CameraPermissionTests.m in Sources */, + 977A25242D5A511600931E34 /* CameraPermissionTests.swift in Sources */, 7D5FCCD42AEF9D0200FB7108 /* CameraSettingsTests.m in Sources */, 7F8FD2292D4BFABF001AF2C1 /* MockGlobalEventApi.m in Sources */, E071CF7427B31DE4006EF3BA /* FLTCamSampleBufferTests.m in Sources */, diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.m deleted file mode 100644 index 095b0453298..00000000000 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.m +++ /dev/null @@ -1,271 +0,0 @@ -// 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 camera_avfoundation; -#if __has_include() -@import camera_avfoundation.Test; -#endif -@import AVFoundation; -@import XCTest; - -#import "CameraTestUtils.h" - -@interface MockPermissionService : NSObject -@property(nonatomic, copy) AVAuthorizationStatus (^authorizationStatusStub)(AVMediaType mediaType); -@property(nonatomic, copy) void (^requestAccessStub)(AVMediaType mediaType, void (^handler)(BOOL)); -@end - -@implementation MockPermissionService -- (AVAuthorizationStatus)authorizationStatusForMediaType:(AVMediaType)mediaType { - return self.authorizationStatusStub ? self.authorizationStatusStub(mediaType) - : AVAuthorizationStatusNotDetermined; -} - -- (void)requestAccessForMediaType:(AVMediaType)mediaType completionHandler:(void (^)(BOOL))handler { - if (self.requestAccessStub) { - self.requestAccessStub(mediaType, handler); - } -} -@end - -@interface FLTCameraPermissionManagerTests : XCTestCase -@property(nonatomic, strong) FLTCameraPermissionManager *permissionManager; -@property(nonatomic, strong) MockPermissionService *mockService; -@end - -@implementation FLTCameraPermissionManagerTests - -- (void)setUp { - [super setUp]; - self.mockService = [[MockPermissionService alloc] init]; - self.permissionManager = - [[FLTCameraPermissionManager alloc] initWithPermissionService:self.mockService]; -} - -#pragma mark - camera permissions - -- (void)testRequestCameraPermission_completeWithoutErrorIfPreviouslyAuthorized { - XCTestExpectation *expectation = - [self expectationWithDescription: - @"Must copmlete without error if camera access was previously authorized."]; - - self.mockService.authorizationStatusStub = ^AVAuthorizationStatus(AVMediaType mediaType) { - XCTAssertEqualObjects(mediaType, AVMediaTypeVideo); - return AVAuthorizationStatusAuthorized; - }; - - [self.permissionManager requestCameraPermissionWithCompletionHandler:^(FlutterError *error) { - if (error == nil) { - [expectation fulfill]; - } - }]; - [self waitForExpectationsWithTimeout:30 handler:nil]; -} -- (void)testRequestCameraPermission_completeWithErrorIfPreviouslyDenied { - XCTestExpectation *expectation = - [self expectationWithDescription: - @"Must complete with error if camera access was previously denied."]; - FlutterError *expectedError = - [FlutterError errorWithCode:@"CameraAccessDeniedWithoutPrompt" - message:@"User has previously denied the camera access request. Go to " - @"Settings to enable camera access." - details:nil]; - - self.mockService.authorizationStatusStub = ^AVAuthorizationStatus(AVMediaType mediaType) { - XCTAssertEqualObjects(mediaType, AVMediaTypeVideo); - return AVAuthorizationStatusDenied; - }; - - [self.permissionManager requestCameraPermissionWithCompletionHandler:^(FlutterError *error) { - if ([error isEqual:expectedError]) { - [expectation fulfill]; - } - }]; - [self waitForExpectationsWithTimeout:30 handler:nil]; -} - -- (void)testRequestCameraPermission_completeWithErrorIfRestricted { - XCTestExpectation *expectation = - [self expectationWithDescription:@"Must complete with error if camera access is restricted."]; - FlutterError *expectedError = [FlutterError errorWithCode:@"CameraAccessRestricted" - message:@"Camera access is restricted. " - details:nil]; - - self.mockService.authorizationStatusStub = ^AVAuthorizationStatus(AVMediaType mediaType) { - XCTAssertEqualObjects(mediaType, AVMediaTypeVideo); - return AVAuthorizationStatusRestricted; - }; - - [self.permissionManager requestCameraPermissionWithCompletionHandler:^(FlutterError *error) { - if ([error isEqual:expectedError]) { - [expectation fulfill]; - } - }]; - [self waitForExpectationsWithTimeout:30 handler:nil]; -} - -- (void)testRequestCameraPermission_completeWithoutErrorIfUserGrantAccess { - XCTestExpectation *grantedExpectation = [self - expectationWithDescription:@"Must complete without error if user choose to grant access"]; - - self.mockService.authorizationStatusStub = ^AVAuthorizationStatus(AVMediaType mediaType) { - XCTAssertEqualObjects(mediaType, AVMediaTypeVideo); - return AVAuthorizationStatusNotDetermined; - }; - - // Mimic user choosing "allow" in permission dialog. - self.mockService.requestAccessStub = ^(AVMediaType mediaType, void (^handler)(BOOL)) { - XCTAssertEqualObjects(mediaType, AVMediaTypeVideo); - handler(YES); - }; - - [self.permissionManager requestCameraPermissionWithCompletionHandler:^(FlutterError *error) { - if (error == nil) { - [grantedExpectation fulfill]; - } - }]; - [self waitForExpectationsWithTimeout:30 handler:nil]; -} - -- (void)testRequestCameraPermission_completeWithErrorIfUserDenyAccess { - XCTestExpectation *expectation = - [self expectationWithDescription:@"Must complete with error if user choose to deny access"]; - FlutterError *expectedError = - [FlutterError errorWithCode:@"CameraAccessDenied" - message:@"User denied the camera access request." - details:nil]; - - self.mockService.authorizationStatusStub = ^AVAuthorizationStatus(AVMediaType mediaType) { - XCTAssertEqualObjects(mediaType, AVMediaTypeVideo); - return AVAuthorizationStatusNotDetermined; - }; - - // Mimic user choosing "deny" in permission dialog. - self.mockService.requestAccessStub = ^(AVMediaType mediaType, void (^handler)(BOOL)) { - XCTAssertEqualObjects(mediaType, AVMediaTypeVideo); - handler(NO); - }; - - [self.permissionManager requestCameraPermissionWithCompletionHandler:^(FlutterError *error) { - if ([error isEqual:expectedError]) { - [expectation fulfill]; - } - }]; - - [self waitForExpectationsWithTimeout:30 handler:nil]; -} - -#pragma mark - audio permissions - -- (void)testRequestAudioPermission_completeWithoutErrorIfPrevoiuslyAuthorized { - XCTestExpectation *expectation = - [self expectationWithDescription: - @"Must copmlete without error if audio access was previously authorized."]; - - self.mockService.authorizationStatusStub = ^AVAuthorizationStatus(AVMediaType mediaType) { - XCTAssertEqualObjects(mediaType, AVMediaTypeAudio); - return AVAuthorizationStatusAuthorized; - }; - - [self.permissionManager requestAudioPermissionWithCompletionHandler:^(FlutterError *error) { - if (error == nil) { - [expectation fulfill]; - } - }]; - [self waitForExpectationsWithTimeout:30 handler:nil]; -} - -- (void)testRequestAudioPermission_completeWithErrorIfPreviouslyDenied { - XCTestExpectation *expectation = - [self expectationWithDescription: - @"Must complete with error if audio access was previously denied."]; - FlutterError *expectedError = - [FlutterError errorWithCode:@"AudioAccessDeniedWithoutPrompt" - message:@"User has previously denied the audio access request. Go to " - @"Settings to enable audio access." - details:nil]; - - self.mockService.authorizationStatusStub = ^AVAuthorizationStatus(AVMediaType mediaType) { - XCTAssertEqualObjects(mediaType, AVMediaTypeAudio); - return AVAuthorizationStatusDenied; - }; - - [self.permissionManager requestAudioPermissionWithCompletionHandler:^(FlutterError *error) { - if ([error isEqual:expectedError]) { - [expectation fulfill]; - } - }]; - [self waitForExpectationsWithTimeout:30 handler:nil]; -} - -- (void)testRequestAudioPermission_completeWithErrorIfRestricted { - XCTestExpectation *expectation = - [self expectationWithDescription:@"Must complete with error if audio access is restricted."]; - FlutterError *expectedError = [FlutterError errorWithCode:@"AudioAccessRestricted" - message:@"Audio access is restricted. " - details:nil]; - - self.mockService.authorizationStatusStub = ^AVAuthorizationStatus(AVMediaType mediaType) { - XCTAssertEqualObjects(mediaType, AVMediaTypeAudio); - return AVAuthorizationStatusRestricted; - }; - - [self.permissionManager requestAudioPermissionWithCompletionHandler:^(FlutterError *error) { - if ([error isEqual:expectedError]) { - [expectation fulfill]; - } - }]; - [self waitForExpectationsWithTimeout:30 handler:nil]; -} - -- (void)testRequestAudioPermission_completeWithoutErrorIfUserGrantAccess { - XCTestExpectation *grantedExpectation = [self - expectationWithDescription:@"Must complete without error if user choose to grant access"]; - - self.mockService.authorizationStatusStub = ^AVAuthorizationStatus(AVMediaType mediaType) { - XCTAssertEqualObjects(mediaType, AVMediaTypeAudio); - return AVAuthorizationStatusNotDetermined; - }; - - // Mimic user choosing "allow" in permission dialog. - self.mockService.requestAccessStub = ^(AVMediaType mediaType, void (^handler)(BOOL)) { - XCTAssertEqualObjects(mediaType, AVMediaTypeAudio); - handler(YES); - }; - - [self.permissionManager requestAudioPermissionWithCompletionHandler:^(FlutterError *error) { - if (error == nil) { - [grantedExpectation fulfill]; - } - }]; - [self waitForExpectationsWithTimeout:30 handler:nil]; -} - -- (void)testRequestAudioPermission_completeWithErrorIfUserDenyAccess { - XCTestExpectation *expectation = - [self expectationWithDescription:@"Must complete with error if user choose to deny access"]; - FlutterError *expectedError = [FlutterError errorWithCode:@"AudioAccessDenied" - message:@"User denied the audio access request." - details:nil]; - - self.mockService.authorizationStatusStub = ^AVAuthorizationStatus(AVMediaType mediaType) { - XCTAssertEqualObjects(mediaType, AVMediaTypeAudio); - return AVAuthorizationStatusNotDetermined; - }; - - // Mimic user choosing "deny" in permission dialog. - self.mockService.requestAccessStub = ^(AVMediaType mediaType, void (^handler)(BOOL)) { - XCTAssertEqualObjects(mediaType, AVMediaTypeAudio); - handler(NO); - }; - - [self.permissionManager requestAudioPermissionWithCompletionHandler:^(FlutterError *error) { - if ([error isEqual:expectedError]) { - [expectation fulfill]; - } - }]; - [self waitForExpectationsWithTimeout:30 handler:nil]; -} - -@end diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.swift new file mode 100644 index 00000000000..757b06b57ac --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.swift @@ -0,0 +1,238 @@ +// 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 AVFoundation +import camera_avfoundation +import XCTest + +final class MockPermissionService: NSObject, FLTPermissionServicing { + var authorizationStatusStub: ((AVMediaType) -> AVAuthorizationStatus)? + // FIXME: Is escaping needed here? + var requestAccessStub: ((AVMediaType, @escaping (Bool) -> Void) -> Void)? + + // FIXME: How about naming here? What does the style guide say about it? + func authorizationStatus(for mediaType: AVMediaType) -> AVAuthorizationStatus { + return authorizationStatusStub?(mediaType) ?? .notDetermined + } + + func requestAccess(for mediaType: AVMediaType, completion: @escaping (Bool) -> Void) { + requestAccessStub?(mediaType, completion) + } +} + +final class FLTCameraPermissionManagerTests: XCTestCase { + var mockService: MockPermissionService! + var permissionManager: FLTCameraPermissionManager! + + override func setUp() { + mockService = MockPermissionService() + permissionManager = FLTCameraPermissionManager(permissionService: mockService) + } + + // MARK: - Camera permissions + + func testRequestCameraPermission_completeWithoutErrorIfPreviouslyAuthorized() { + let expectation = self.expectation(description: "Must complete without error if camera access was previously authorized.") + + mockService.authorizationStatusStub = { mediaType in + XCTAssertEqual(mediaType, .video) + return .authorized + } + + permissionManager.requestCameraPermission { error in + XCTAssertNil(error) + expectation.fulfill() + } + + waitForExpectations(timeout: 30, handler: nil) + } + + func testRequestCameraPermission_completeWithErrorIfPreviouslyDenied() { + let expectation = self.expectation(description: "Must complete with error if camera access was previously denied.") + let expectedError = FlutterError(code: "CameraAccessDeniedWithoutPrompt", + message: "User has previously denied the camera access request. Go to Settings to enable camera access.", + details: nil) + + mockService.authorizationStatusStub = { mediaType in + XCTAssertEqual(mediaType, .video) + return .denied + } + + permissionManager.requestCameraPermission { error in + XCTAssertEqual(error, expectedError) + expectation.fulfill() + } + + waitForExpectations(timeout: 30, handler: nil) + } + + func testRequestCameraPermission_completeWithErrorIfRestricted() { + let expectation = self.expectation(description: "Must complete with error if camera access is restricted.") + let expectedError = FlutterError(code: "CameraAccessRestricted", + message: "Camera access is restricted.", + details: nil) + + mockService.authorizationStatusStub = { mediaType in + XCTAssertEqual(mediaType, .video) + return .restricted + } + + permissionManager.requestCameraPermission { error in + XCTAssertEqual(error, expectedError) + expectation.fulfill() + } + + waitForExpectations(timeout: 30, handler: nil) + } + + func testRequestCameraPermission_completeWithoutErrorIfUserGrantAccess() { + let expectation = self.expectation(description: "Must complete without error if user granted access.") + + mockService.authorizationStatusStub = { mediaType in + XCTAssertEqual(mediaType, .video) + return .notDetermined + } + + mockService.requestAccessStub = { mediaType, handler in + XCTAssertEqual(mediaType, .video) + // Grant access. + handler(true) + } + + permissionManager.requestCameraPermission { error in + XCTAssertNil(error) + expectation.fulfill() + } + + waitForExpectations(timeout: 30, handler: nil) + } + + func testRequestCameraPermission_completeWithErrorIfUserDenyAccess() { + let expectation = self.expectation(description: "Must complete with error if user denied access.") + let expectedError = FlutterError(code: "CameraAccessDenied", + message: "User denied the camera access request.", + details: nil) + + mockService.authorizationStatusStub = { mediaType in + XCTAssertEqual(mediaType, .video) + return .notDetermined + } + + mockService.requestAccessStub = { mediaType, handler in + XCTAssertEqual(mediaType, .video) + // Deny access. + handler(false) + } + + permissionManager.requestCameraPermission { error in + XCTAssertEqual(error, expectedError) + expectation.fulfill() + } + + waitForExpectations(timeout: 30, handler: nil) + } + + // MARK: - Audio permissions + + func testRequestAudioPermission_completeWithoutErrorIfPreviouslyAuthorized() { + let expectation = self.expectation(description: "Must complete without error if audio access was previously authorized.") + + mockService.authorizationStatusStub = { mediaType in + XCTAssertEqual(mediaType, .audio) + return .authorized + } + + permissionManager.requestAudioPermission { error in + XCTAssertNil(error) + expectation.fulfill() + } + + waitForExpectations(timeout: 30, handler: nil) + } + + func testRequestAudioPermission_completeWithErrorIfPreviouslyDenied() { + let expectation = self.expectation(description: "Must complete with error if audio access was previously denied.") + let expectedError = FlutterError(code: "AudioAccessDeniedWithoutPrompt", + message: "User has previously denied the audio access request. Go to Settings to enable audio access.", + details: nil) + + mockService.authorizationStatusStub = { mediaType in + XCTAssertEqual(mediaType, .audio) + return .denied + } + + permissionManager.requestAudioPermission { error in + XCTAssertEqual(error, expectedError) + expectation.fulfill() + } + + waitForExpectations(timeout: 30, handler: nil) + } + + func testRequestAudioPermission_completeWithErrorIfRestricted() { + let expectation = self.expectation(description: "Must complete with error if audio access is restricted.") + let expectedError = FlutterError(code: "AudioAccessRestricted", + message: "Audio access is restricted.", + details: nil) + + mockService.authorizationStatusStub = { mediaType in + XCTAssertEqual(mediaType, .audio) + return .restricted + } + + permissionManager.requestAudioPermission { error in + XCTAssertEqual(error, expectedError) + expectation.fulfill() + } + + waitForExpectations(timeout: 30, handler: nil) + } + + func testRequestAudioPermission_completeWithoutErrorIfUserGrantAccess() { + let expectation = self.expectation(description: "Must complete without error if user granted access.") + + mockService.authorizationStatusStub = { mediaType in + XCTAssertEqual(mediaType, .audio) + return .notDetermined + } + + mockService.requestAccessStub = { mediaType, handler in + XCTAssertEqual(mediaType, .audio) + // Grant access. + handler(true) + } + + permissionManager.requestAudioPermission { error in + XCTAssertNil(error) + expectation.fulfill() + } + + waitForExpectations(timeout: 30, handler: nil) + } + + func testRequestAudioPermission_completeWithErrorIfUserDenyAccess() { + let expectation = self.expectation(description: "Must complete with error if user denied access") + let expectedError = FlutterError(code: "AudioAccessDenied", + message: "User denied the audio access request.", + details: nil) + + mockService.authorizationStatusStub = { mediaType in + XCTAssertEqual(mediaType, .audio) + return .notDetermined + } + + mockService.requestAccessStub = { mediaType, handler in + XCTAssertEqual(mediaType, .audio) + // Deny access. + handler(false) + } + + permissionManager.requestAudioPermission { error in + XCTAssertEqual(error, expectedError) + expectation.fulfill() + } + + waitForExpectations(timeout: 30, handler: nil) + } +} diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/FLTCameraPermissionManager.m b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/FLTCameraPermissionManager.m index 013326a0de3..c028a98a778 100644 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/FLTCameraPermissionManager.m +++ b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/FLTCameraPermissionManager.m @@ -61,11 +61,11 @@ - (void)requestPermissionForAudio:(BOOL)forAudio FlutterError *flutterError; if (forAudio) { flutterError = [FlutterError errorWithCode:@"AudioAccessRestricted" - message:@"Audio access is restricted. " + message:@"Audio access is restricted." details:nil]; } else { flutterError = [FlutterError errorWithCode:@"CameraAccessRestricted" - message:@"Camera access is restricted. " + message:@"Camera access is restricted." details:nil]; } handler(flutterError); diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/include/camera_avfoundation/FLTPermissionServicing.h b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/include/camera_avfoundation/FLTPermissionServicing.h index 2e65568baf8..bdec5fcb1e2 100644 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/include/camera_avfoundation/FLTPermissionServicing.h +++ b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/include/camera_avfoundation/FLTPermissionServicing.h @@ -8,9 +8,11 @@ NS_ASSUME_NONNULL_BEGIN @protocol FLTPermissionServicing -- (AVAuthorizationStatus)authorizationStatusForMediaType:(AVMediaType)mediaType; +- (AVAuthorizationStatus)authorizationStatusForMediaType:(AVMediaType)mediaType + NS_SWIFT_NAME(authorizationStatus(for:)); - (void)requestAccessForMediaType:(AVMediaType)mediaType - completionHandler:(void (^)(BOOL granted))handler; + completionHandler:(void (^)(BOOL granted))handler + NS_SWIFT_NAME(requestAccess(for:completion:)); @end @interface FLTDefaultPermissionService : NSObject From aab1d5ef5d818e7719d85323a567008739b4b667 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 11 Feb 2025 11:07:19 +0100 Subject: [PATCH 08/25] Format file --- .../example/ios/RunnerTests/CameraTestUtils.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.m index 8b12a773e4f..dbb247b5333 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.m @@ -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 "CameraTestUtils.h" +#import "CameraTestUtils.h" #import @import AVFoundation; From 06a7bd666f564c5ac89b359c705a20ae4b46bd82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 11 Feb 2025 11:08:28 +0100 Subject: [PATCH 09/25] Bump version and update changelog --- packages/camera/camera_avfoundation/CHANGELOG.md | 4 ++++ packages/camera/camera_avfoundation/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/camera/camera_avfoundation/CHANGELOG.md b/packages/camera/camera_avfoundation/CHANGELOG.md index 2388f101e24..6b2e63ff95e 100644 --- a/packages/camera/camera_avfoundation/CHANGELOG.md +++ b/packages/camera/camera_avfoundation/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.18+5 + +* Migrates unit tests to Swift. + ## 0.9.18+4 * Refactors implementations to reduce usage of OCMock in internal testing. diff --git a/packages/camera/camera_avfoundation/pubspec.yaml b/packages/camera/camera_avfoundation/pubspec.yaml index 56cd44cc6ac..2b858f7e30b 100644 --- a/packages/camera/camera_avfoundation/pubspec.yaml +++ b/packages/camera/camera_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_avfoundation description: iOS implementation of the camera plugin. repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.18+4 +version: 0.9.18+5 environment: sdk: ^3.4.0 From cb937d63228ddcdb9a084535427e9701d840f0fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 11 Feb 2025 11:44:08 +0100 Subject: [PATCH 10/25] Format Swift files --- .../RunnerTests/AvailableCamerasTests.swift | 10 ++- .../ios/RunnerTests/CameraExposureTests.swift | 2 +- .../ios/RunnerTests/CameraFocusTests.swift | 2 +- .../RunnerTests/CameraPermissionTests.swift | 76 +++++++++++------- .../RunnerTests/CameraPreviewPauseTests.swift | 2 +- .../RunnerTests/CameraPropertiesTests.swift | 77 +++++++++++-------- .../ios/RunnerTests/QueueUtilsTests.swift | 2 +- 7 files changed, 105 insertions(+), 66 deletions(-) diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTests.swift index f83193e7605..ca390d080ed 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTests.swift @@ -3,8 +3,8 @@ // found in the LICENSE file. import AVFoundation -import camera_avfoundation import XCTest +import camera_avfoundation final class AvailableCamerasTest: XCTestCase { func createCameraPlugin(_ deviceDiscoverer: MockCameraDeviceDiscoverer) -> CameraPlugin { @@ -42,7 +42,9 @@ final class AvailableCamerasTest: XCTestCase { telephotoCamera.uniqueID = "3" telephotoCamera.position = .back - var requiredTypes: [AVCaptureDevice.DeviceType] = [.builtInWideAngleCamera, .builtInTelephotoCamera] + var requiredTypes: [AVCaptureDevice.DeviceType] = [ + .builtInWideAngleCamera, .builtInTelephotoCamera, + ] if #available(iOS 13.0, *) { requiredTypes.append(.builtInUltraWideCamera) } @@ -90,7 +92,9 @@ final class AvailableCamerasTest: XCTestCase { frontFacingCamera.uniqueID = "1" frontFacingCamera.position = .front - var requiredTypes: [AVCaptureDevice.DeviceType] = [.builtInWideAngleCamera, .builtInTelephotoCamera] + var requiredTypes: [AVCaptureDevice.DeviceType] = [ + .builtInWideAngleCamera, .builtInTelephotoCamera, + ] if #available(iOS 13.0, *) { requiredTypes.append(.builtInUltraWideCamera) } diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.swift index 5e621c5a9e3..055173252da 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.swift @@ -3,8 +3,8 @@ // found in the LICENSE file. import AVFoundation -import camera_avfoundation import XCTest +import camera_avfoundation final class CameraExposureTests: XCTestCase { var camera: FLTCam! diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.swift index 07c1443de25..3d323c65827 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.swift @@ -3,8 +3,8 @@ // found in the LICENSE file. import AVFoundation -import camera_avfoundation import XCTest +import camera_avfoundation final class CameraFocusTests: XCTestCase { var camera: FLTCam! diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.swift index 757b06b57ac..85ac4a8979c 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.swift @@ -3,8 +3,8 @@ // found in the LICENSE file. import AVFoundation -import camera_avfoundation import XCTest +import camera_avfoundation final class MockPermissionService: NSObject, FLTPermissionServicing { var authorizationStatusStub: ((AVMediaType) -> AVAuthorizationStatus)? @@ -33,7 +33,8 @@ final class FLTCameraPermissionManagerTests: XCTestCase { // MARK: - Camera permissions func testRequestCameraPermission_completeWithoutErrorIfPreviouslyAuthorized() { - let expectation = self.expectation(description: "Must complete without error if camera access was previously authorized.") + let expectation = self.expectation( + description: "Must complete without error if camera access was previously authorized.") mockService.authorizationStatusStub = { mediaType in XCTAssertEqual(mediaType, .video) @@ -49,10 +50,13 @@ final class FLTCameraPermissionManagerTests: XCTestCase { } func testRequestCameraPermission_completeWithErrorIfPreviouslyDenied() { - let expectation = self.expectation(description: "Must complete with error if camera access was previously denied.") - let expectedError = FlutterError(code: "CameraAccessDeniedWithoutPrompt", - message: "User has previously denied the camera access request. Go to Settings to enable camera access.", - details: nil) + let expectation = self.expectation( + description: "Must complete with error if camera access was previously denied.") + let expectedError = FlutterError( + code: "CameraAccessDeniedWithoutPrompt", + message: + "User has previously denied the camera access request. Go to Settings to enable camera access.", + details: nil) mockService.authorizationStatusStub = { mediaType in XCTAssertEqual(mediaType, .video) @@ -68,10 +72,12 @@ final class FLTCameraPermissionManagerTests: XCTestCase { } func testRequestCameraPermission_completeWithErrorIfRestricted() { - let expectation = self.expectation(description: "Must complete with error if camera access is restricted.") - let expectedError = FlutterError(code: "CameraAccessRestricted", - message: "Camera access is restricted.", - details: nil) + let expectation = self.expectation( + description: "Must complete with error if camera access is restricted.") + let expectedError = FlutterError( + code: "CameraAccessRestricted", + message: "Camera access is restricted.", + details: nil) mockService.authorizationStatusStub = { mediaType in XCTAssertEqual(mediaType, .video) @@ -87,7 +93,8 @@ final class FLTCameraPermissionManagerTests: XCTestCase { } func testRequestCameraPermission_completeWithoutErrorIfUserGrantAccess() { - let expectation = self.expectation(description: "Must complete without error if user granted access.") + let expectation = self.expectation( + description: "Must complete without error if user granted access.") mockService.authorizationStatusStub = { mediaType in XCTAssertEqual(mediaType, .video) @@ -109,10 +116,12 @@ final class FLTCameraPermissionManagerTests: XCTestCase { } func testRequestCameraPermission_completeWithErrorIfUserDenyAccess() { - let expectation = self.expectation(description: "Must complete with error if user denied access.") - let expectedError = FlutterError(code: "CameraAccessDenied", - message: "User denied the camera access request.", - details: nil) + let expectation = self.expectation( + description: "Must complete with error if user denied access.") + let expectedError = FlutterError( + code: "CameraAccessDenied", + message: "User denied the camera access request.", + details: nil) mockService.authorizationStatusStub = { mediaType in XCTAssertEqual(mediaType, .video) @@ -136,7 +145,8 @@ final class FLTCameraPermissionManagerTests: XCTestCase { // MARK: - Audio permissions func testRequestAudioPermission_completeWithoutErrorIfPreviouslyAuthorized() { - let expectation = self.expectation(description: "Must complete without error if audio access was previously authorized.") + let expectation = self.expectation( + description: "Must complete without error if audio access was previously authorized.") mockService.authorizationStatusStub = { mediaType in XCTAssertEqual(mediaType, .audio) @@ -152,10 +162,13 @@ final class FLTCameraPermissionManagerTests: XCTestCase { } func testRequestAudioPermission_completeWithErrorIfPreviouslyDenied() { - let expectation = self.expectation(description: "Must complete with error if audio access was previously denied.") - let expectedError = FlutterError(code: "AudioAccessDeniedWithoutPrompt", - message: "User has previously denied the audio access request. Go to Settings to enable audio access.", - details: nil) + let expectation = self.expectation( + description: "Must complete with error if audio access was previously denied.") + let expectedError = FlutterError( + code: "AudioAccessDeniedWithoutPrompt", + message: + "User has previously denied the audio access request. Go to Settings to enable audio access.", + details: nil) mockService.authorizationStatusStub = { mediaType in XCTAssertEqual(mediaType, .audio) @@ -171,10 +184,12 @@ final class FLTCameraPermissionManagerTests: XCTestCase { } func testRequestAudioPermission_completeWithErrorIfRestricted() { - let expectation = self.expectation(description: "Must complete with error if audio access is restricted.") - let expectedError = FlutterError(code: "AudioAccessRestricted", - message: "Audio access is restricted.", - details: nil) + let expectation = self.expectation( + description: "Must complete with error if audio access is restricted.") + let expectedError = FlutterError( + code: "AudioAccessRestricted", + message: "Audio access is restricted.", + details: nil) mockService.authorizationStatusStub = { mediaType in XCTAssertEqual(mediaType, .audio) @@ -190,7 +205,8 @@ final class FLTCameraPermissionManagerTests: XCTestCase { } func testRequestAudioPermission_completeWithoutErrorIfUserGrantAccess() { - let expectation = self.expectation(description: "Must complete without error if user granted access.") + let expectation = self.expectation( + description: "Must complete without error if user granted access.") mockService.authorizationStatusStub = { mediaType in XCTAssertEqual(mediaType, .audio) @@ -212,10 +228,12 @@ final class FLTCameraPermissionManagerTests: XCTestCase { } func testRequestAudioPermission_completeWithErrorIfUserDenyAccess() { - let expectation = self.expectation(description: "Must complete with error if user denied access") - let expectedError = FlutterError(code: "AudioAccessDenied", - message: "User denied the audio access request.", - details: nil) + let expectation = self.expectation( + description: "Must complete with error if user denied access") + let expectedError = FlutterError( + code: "AudioAccessDenied", + message: "User denied the audio access request.", + details: nil) mockService.authorizationStatusStub = { mediaType in XCTAssertEqual(mediaType, .audio) diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.swift index 159bfbb99bd..58849f3d507 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.swift @@ -3,8 +3,8 @@ // found in the LICENSE file. import AVFoundation -import camera_avfoundation import XCTest +import camera_avfoundation final class CameraPreviewPauseTests: XCTestCase { private var camera: FLTCam! diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.swift index 6082c761b95..dc8d6127d16 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.swift @@ -3,56 +3,73 @@ // found in the LICENSE file. import AVFoundation -import camera_avfoundation import XCTest +import camera_avfoundation final class CameraPropertiesTests: XCTestCase { // MARK: - Flash Mode Tests func testGetAVCaptureFlashModeForPigeonFlashMode() { - XCTAssertEqual(AVCaptureDevice.FlashMode.off, - FCPGetAVCaptureFlashModeForPigeonFlashMode(FCPPlatformFlashMode.off)) - XCTAssertEqual(AVCaptureDevice.FlashMode.auto, - FCPGetAVCaptureFlashModeForPigeonFlashMode(FCPPlatformFlashMode.auto)) - XCTAssertEqual(AVCaptureDevice.FlashMode.on, - FCPGetAVCaptureFlashModeForPigeonFlashMode(FCPPlatformFlashMode.always)) + XCTAssertEqual( + AVCaptureDevice.FlashMode.off, + FCPGetAVCaptureFlashModeForPigeonFlashMode(FCPPlatformFlashMode.off)) + XCTAssertEqual( + AVCaptureDevice.FlashMode.auto, + FCPGetAVCaptureFlashModeForPigeonFlashMode(FCPPlatformFlashMode.auto)) + XCTAssertEqual( + AVCaptureDevice.FlashMode.on, + FCPGetAVCaptureFlashModeForPigeonFlashMode(FCPPlatformFlashMode.always)) // FIXME: Migrate implementation to Swift error to test this -// XCTAssertThrowsError(FCPGetAVCaptureFlashModeForPigeonFlashMode(FCPPlatformFlashMode.torch)) + // XCTAssertThrowsError(FCPGetAVCaptureFlashModeForPigeonFlashMode(FCPPlatformFlashMode.torch)) } // MARK: - Video Format Tests func testGetPixelFormatForPigeonFormat() { - XCTAssertEqual(kCVPixelFormatType_32BGRA, - FCPGetPixelFormatForPigeonFormat(FCPPlatformImageFormatGroup.bgra8888)) - XCTAssertEqual(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, - FCPGetPixelFormatForPigeonFormat(FCPPlatformImageFormatGroup.yuv420)) + XCTAssertEqual( + kCVPixelFormatType_32BGRA, + FCPGetPixelFormatForPigeonFormat(FCPPlatformImageFormatGroup.bgra8888)) + XCTAssertEqual( + kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, + FCPGetPixelFormatForPigeonFormat(FCPPlatformImageFormatGroup.yuv420)) } // MARK: - Device Orientation Tests func testGetUIDeviceOrientationForPigeonDeviceOrientation() { - XCTAssertEqual(UIDeviceOrientation.portraitUpsideDown, - FCPGetUIDeviceOrientationForPigeonDeviceOrientation(FCPPlatformDeviceOrientation.portraitDown)) - XCTAssertEqual(UIDeviceOrientation.landscapeLeft, - FCPGetUIDeviceOrientationForPigeonDeviceOrientation(FCPPlatformDeviceOrientation.landscapeLeft)) - XCTAssertEqual(UIDeviceOrientation.landscapeRight, - FCPGetUIDeviceOrientationForPigeonDeviceOrientation(FCPPlatformDeviceOrientation.landscapeRight)) - XCTAssertEqual(UIDeviceOrientation.portrait, - FCPGetUIDeviceOrientationForPigeonDeviceOrientation(FCPPlatformDeviceOrientation.portraitUp)) + XCTAssertEqual( + UIDeviceOrientation.portraitUpsideDown, + FCPGetUIDeviceOrientationForPigeonDeviceOrientation(FCPPlatformDeviceOrientation.portraitDown) + ) + XCTAssertEqual( + UIDeviceOrientation.landscapeLeft, + FCPGetUIDeviceOrientationForPigeonDeviceOrientation( + FCPPlatformDeviceOrientation.landscapeLeft)) + XCTAssertEqual( + UIDeviceOrientation.landscapeRight, + FCPGetUIDeviceOrientationForPigeonDeviceOrientation( + FCPPlatformDeviceOrientation.landscapeRight)) + XCTAssertEqual( + UIDeviceOrientation.portrait, + FCPGetUIDeviceOrientationForPigeonDeviceOrientation(FCPPlatformDeviceOrientation.portraitUp)) } func testGetPigeonDeviceOrientationForUIDeviceOrientation() { - XCTAssertEqual(FCPPlatformDeviceOrientation.portraitDown, - FCPGetPigeonDeviceOrientationForOrientation(UIDeviceOrientation.portraitUpsideDown)) - XCTAssertEqual(FCPPlatformDeviceOrientation.landscapeLeft, - FCPGetPigeonDeviceOrientationForOrientation(UIDeviceOrientation.landscapeLeft)) - XCTAssertEqual(FCPPlatformDeviceOrientation.landscapeRight, - FCPGetPigeonDeviceOrientationForOrientation(UIDeviceOrientation.landscapeRight)) - XCTAssertEqual(FCPPlatformDeviceOrientation.portraitUp, - FCPGetPigeonDeviceOrientationForOrientation(UIDeviceOrientation.portrait)) + XCTAssertEqual( + FCPPlatformDeviceOrientation.portraitDown, + FCPGetPigeonDeviceOrientationForOrientation(UIDeviceOrientation.portraitUpsideDown)) + XCTAssertEqual( + FCPPlatformDeviceOrientation.landscapeLeft, + FCPGetPigeonDeviceOrientationForOrientation(UIDeviceOrientation.landscapeLeft)) + XCTAssertEqual( + FCPPlatformDeviceOrientation.landscapeRight, + FCPGetPigeonDeviceOrientationForOrientation(UIDeviceOrientation.landscapeRight)) + XCTAssertEqual( + FCPPlatformDeviceOrientation.portraitUp, + FCPGetPigeonDeviceOrientationForOrientation(UIDeviceOrientation.portrait)) // Test default case. - XCTAssertEqual(FCPPlatformDeviceOrientation.portraitUp, - FCPGetPigeonDeviceOrientationForOrientation(UIDeviceOrientation.unknown)) + XCTAssertEqual( + FCPPlatformDeviceOrientation.portraitUp, + FCPGetPigeonDeviceOrientationForOrientation(UIDeviceOrientation.unknown)) } } diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/QueueUtilsTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/QueueUtilsTests.swift index 1938179eb84..de73eb62d64 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/QueueUtilsTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/QueueUtilsTests.swift @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import camera_avfoundation import XCTest +import camera_avfoundation final class QueueUtilsTests: XCTestCase { func testShouldStayOnMainQueueIfCalledFromMainQueue() { From 8d10061e831c9d9f52c72ce1b2e740909ce56a65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 11 Feb 2025 12:35:29 +0100 Subject: [PATCH 11/25] Handle catching Objective-C errors in Swift tests --- .../ios/Runner.xcodeproj/project.pbxproj | 13 ++++++++---- .../RunnerTests/CameraPermissionTests.swift | 2 -- .../RunnerTests/CameraPropertiesTests.swift | 9 ++++++-- .../ios/RunnerTests/ExceptionCatcher.h | 21 +++++++++++++++++++ .../ios/RunnerTests/ExceptionCatcher.m | 18 ++++++++++++++++ .../RunnerTests/RunnerTests-Bridging-Header.h | 12 +++++------ 6 files changed, 61 insertions(+), 14 deletions(-) create mode 100644 packages/camera/camera_avfoundation/example/ios/RunnerTests/ExceptionCatcher.h create mode 100644 packages/camera/camera_avfoundation/example/ios/RunnerTests/ExceptionCatcher.m diff --git a/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj index 2c976c9ec96..9a20f50f097 100644 --- a/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj @@ -25,9 +25,9 @@ 7F8FD22F2D4D0B88001AF2C1 /* MockFlutterBinaryMessenger.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F8FD22E2D4D0B88001AF2C1 /* MockFlutterBinaryMessenger.m */; }; 7FCEDD352D43C2B900EA1CA8 /* MockDeviceOrientationProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FCEDD342D43C2B900EA1CA8 /* MockDeviceOrientationProvider.m */; }; 7FCEDD362D43C2B900EA1CA8 /* MockCaptureDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FCEDD322D43C2B900EA1CA8 /* MockCaptureDevice.m */; }; - 7FD582352D57D97C003B1200 /* MockCaptureDeviceFormat.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FD582342D57D97C003B1200 /* MockCaptureDeviceFormat.m */; }; 7FD582122D579650003B1200 /* MockWritableData.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FD582112D579650003B1200 /* MockWritableData.m */; }; 7FD582202D579ECC003B1200 /* MockCapturePhotoOutput.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FD5821F2D579ECC003B1200 /* MockCapturePhotoOutput.m */; }; + 7FD582352D57D97C003B1200 /* MockCaptureDeviceFormat.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FD582342D57D97C003B1200 /* MockCaptureDeviceFormat.m */; }; 972CA92B2D5A1D8C004B846F /* CameraPropertiesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 972CA92A2D5A1D8C004B846F /* CameraPropertiesTests.swift */; }; 972CA92D2D5A28C4004B846F /* QueueUtilsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 972CA92C2D5A28C4004B846F /* QueueUtilsTests.swift */; }; 972CA9312D5A366C004B846F /* CameraExposureTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 972CA9302D5A366C004B846F /* CameraExposureTests.swift */; }; @@ -35,6 +35,7 @@ 977A25222D5A49EC00931E34 /* CameraFocusTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 977A25212D5A49EC00931E34 /* CameraFocusTests.swift */; }; 977A25242D5A511600931E34 /* CameraPermissionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 977A25232D5A511600931E34 /* CameraPermissionTests.swift */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; + 979B3DFB2D5B6BC7009BDE1A /* ExceptionCatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 979B3DFA2D5B6BC7009BDE1A /* ExceptionCatcher.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; @@ -101,12 +102,12 @@ 7FCEDD322D43C2B900EA1CA8 /* MockCaptureDevice.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MockCaptureDevice.m; sourceTree = ""; }; 7FCEDD332D43C2B900EA1CA8 /* MockDeviceOrientationProvider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockDeviceOrientationProvider.h; sourceTree = ""; }; 7FCEDD342D43C2B900EA1CA8 /* MockDeviceOrientationProvider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MockDeviceOrientationProvider.m; sourceTree = ""; }; - 7FD582342D57D97C003B1200 /* MockCaptureDeviceFormat.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MockCaptureDeviceFormat.m; sourceTree = ""; }; - 7FD582362D57D989003B1200 /* MockCaptureDeviceFormat.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockCaptureDeviceFormat.h; sourceTree = ""; }; 7FD582112D579650003B1200 /* MockWritableData.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MockWritableData.m; sourceTree = ""; }; 7FD582132D57965A003B1200 /* MockWritableData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockWritableData.h; sourceTree = ""; }; 7FD5821F2D579ECC003B1200 /* MockCapturePhotoOutput.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MockCapturePhotoOutput.m; sourceTree = ""; }; 7FD582212D579ED9003B1200 /* MockCapturePhotoOutput.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockCapturePhotoOutput.h; sourceTree = ""; }; + 7FD582342D57D97C003B1200 /* MockCaptureDeviceFormat.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MockCaptureDeviceFormat.m; sourceTree = ""; }; + 7FD582362D57D989003B1200 /* MockCaptureDeviceFormat.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockCaptureDeviceFormat.h; sourceTree = ""; }; 972CA92A2D5A1D8C004B846F /* CameraPropertiesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraPropertiesTests.swift; sourceTree = ""; }; 972CA92C2D5A28C4004B846F /* QueueUtilsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueUtilsTests.swift; sourceTree = ""; }; 972CA9302D5A366C004B846F /* CameraExposureTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraExposureTests.swift; sourceTree = ""; }; @@ -115,6 +116,8 @@ 977A251F2D5A439300931E34 /* AvailableCamerasTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvailableCamerasTests.swift; sourceTree = ""; }; 977A25212D5A49EC00931E34 /* CameraFocusTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraFocusTests.swift; sourceTree = ""; }; 977A25232D5A511600931E34 /* CameraPermissionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraPermissionTests.swift; sourceTree = ""; }; + 979B3DF92D5B6BA2009BDE1A /* ExceptionCatcher.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ExceptionCatcher.h; sourceTree = ""; }; + 979B3DFA2D5B6BC7009BDE1A /* ExceptionCatcher.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ExceptionCatcher.m; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; @@ -183,6 +186,8 @@ 977A251F2D5A439300931E34 /* AvailableCamerasTests.swift */, 977A25212D5A49EC00931E34 /* CameraFocusTests.swift */, 977A25232D5A511600931E34 /* CameraPermissionTests.swift */, + 979B3DF92D5B6BA2009BDE1A /* ExceptionCatcher.h */, + 979B3DFA2D5B6BC7009BDE1A /* ExceptionCatcher.m */, ); path = RunnerTests; sourceTree = ""; @@ -513,6 +518,7 @@ files = ( 033B94BE269C40A200B4DF97 /* CameraMethodChannelTests.m in Sources */, 972CA92D2D5A28C4004B846F /* QueueUtilsTests.swift in Sources */, + 979B3DFB2D5B6BC7009BDE1A /* ExceptionCatcher.m in Sources */, E071CF7227B3061B006EF3BA /* FLTCamPhotoCaptureTests.m in Sources */, 977A25242D5A511600931E34 /* CameraPermissionTests.swift in Sources */, 7D5FCCD42AEF9D0200FB7108 /* CameraSettingsTests.m in Sources */, @@ -535,7 +541,6 @@ E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */, 977A25222D5A49EC00931E34 /* CameraFocusTests.swift in Sources */, 7F29EB412D281C7E00740257 /* MockCaptureSession.m in Sources */, - E01EE4A82799F3A5008C1950 /* QueueUtilsTests.m in Sources */, 7FD582122D579650003B1200 /* MockWritableData.m in Sources */, 7FCEDD352D43C2B900EA1CA8 /* MockDeviceOrientationProvider.m in Sources */, 7FCEDD362D43C2B900EA1CA8 /* MockCaptureDevice.m in Sources */, diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.swift index 85ac4a8979c..ff11df4b049 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.swift @@ -8,10 +8,8 @@ import camera_avfoundation final class MockPermissionService: NSObject, FLTPermissionServicing { var authorizationStatusStub: ((AVMediaType) -> AVAuthorizationStatus)? - // FIXME: Is escaping needed here? var requestAccessStub: ((AVMediaType, @escaping (Bool) -> Void) -> Void)? - // FIXME: How about naming here? What does the style guide say about it? func authorizationStatus(for mediaType: AVMediaType) -> AVAuthorizationStatus { return authorizationStatusStub?(mediaType) ?? .notDetermined } diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.swift index dc8d6127d16..a408f1b74ee 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.swift @@ -3,6 +3,7 @@ // found in the LICENSE file. import AVFoundation +import Foundation import XCTest import camera_avfoundation @@ -19,8 +20,12 @@ final class CameraPropertiesTests: XCTestCase { XCTAssertEqual( AVCaptureDevice.FlashMode.on, FCPGetAVCaptureFlashModeForPigeonFlashMode(FCPPlatformFlashMode.always)) - // FIXME: Migrate implementation to Swift error to test this - // XCTAssertThrowsError(FCPGetAVCaptureFlashModeForPigeonFlashMode(FCPPlatformFlashMode.torch)) + + // TODO(FirentisTFW): Migrate implementation to throw Swift error in this case + let exception = ExceptionCatcher.catchException { + _ = FCPGetAVCaptureFlashModeForPigeonFlashMode(.torch) + } + XCTAssertNotNil(exception) } // MARK: - Video Format Tests diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/ExceptionCatcher.h b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ExceptionCatcher.h new file mode 100644 index 00000000000..326b39e8d26 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ExceptionCatcher.h @@ -0,0 +1,21 @@ +// 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. + +// TODO(FirentisTFW): Remove this file when the plugin code using it is migrated to Swift. + +#import + +NS_ASSUME_NONNULL_BEGIN + +/// A utility class for catching Objective-C exceptions. +/// +/// It allows to execute a block of code and catch any exceptions that are thrown during its +/// execution. This is useful for bridging between Objective-C and Swift code, as Swift does not +/// support catching Objective-C exceptions directly. +@interface ExceptionCatcher : NSObject +/// Executes a block of code and catches any exceptions that are thrown. ++ (nullable NSException *)catchException:(void (^)(void))tryBlock; +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/ExceptionCatcher.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ExceptionCatcher.m new file mode 100644 index 00000000000..cfed009486b --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ExceptionCatcher.m @@ -0,0 +1,18 @@ +// 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 "ExceptionCatcher.h" + +@implementation ExceptionCatcher + ++ (nullable NSException *)catchException:(void (^)(void))tryBlock { + @try { + tryBlock(); + return nil; // No exception occurred + } @catch (NSException *exception) { + return exception; // Return the caught exception + } +} + +@end diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/RunnerTests-Bridging-Header.h b/packages/camera/camera_avfoundation/example/ios/RunnerTests/RunnerTests-Bridging-Header.h index 8ce23cb76bc..5a3f4609c5d 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/RunnerTests-Bridging-Header.h +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/RunnerTests-Bridging-Header.h @@ -2,18 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// FIXME Verify if all these imports are needed + sort imports +// Sources. #import "camera_avfoundation/CameraPlugin.h" #import "camera_avfoundation/CameraPlugin_Test.h" #import "camera_avfoundation/FLTCam.h" #import "camera_avfoundation/FLTCamConfiguration.h" -#import "camera_avfoundation/FLTCam_Test.h" -#import "camera_avfoundation/FLTSavePhotoDelegate.h" -#import "camera_avfoundation/FLTSavePhotoDelegate_Test.h" #import "camera_avfoundation/FLTThreadSafeEventChannel.h" -// Mocks, utils, protocols -#import "CameraTestUtils.h" +// Mocks, protocols. #import "MockCameraDeviceDiscoverer.h" #import "MockCaptureDevice.h" #import "MockCaptureSession.h" @@ -22,3 +18,7 @@ #import "MockFlutterBinaryMessenger.h" #import "MockFlutterTextureRegistry.h" #import "MockGlobalEventApi.h" + +// Utils. +#import "CameraTestUtils.h" +#import "ExceptionCatcher.h" From a314c5b9c831d223d2a1a6a84565182784294925 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 11 Feb 2025 14:08:54 +0100 Subject: [PATCH 12/25] Fix expectation timeout after rebase --- .../example/ios/RunnerTests/CameraFocusTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.swift index 3d323c65827..5dd34c3e8b0 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.swift @@ -150,6 +150,6 @@ final class CameraFocusTests: XCTestCase { expectation.fulfill() } - waitForExpectations(timeout: 1, handler: nil) + waitForExpectations(timeout: 30, handler: nil) } } From 6d4bf8d99e5b17c5ffc9713a7460fd2cfde93ada Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 11 Feb 2025 14:10:14 +0100 Subject: [PATCH 13/25] Remove redundant comments --- .../example/ios/RunnerTests/ExceptionCatcher.m | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/ExceptionCatcher.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ExceptionCatcher.m index cfed009486b..914447786fc 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/ExceptionCatcher.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ExceptionCatcher.m @@ -9,9 +9,10 @@ @implementation ExceptionCatcher + (nullable NSException *)catchException:(void (^)(void))tryBlock { @try { tryBlock(); - return nil; // No exception occurred + // No exception occurred. + return nil; } @catch (NSException *exception) { - return exception; // Return the caught exception + return exception; } } From fc340062d8d616ef668bd039381f35f7d27f77bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 11 Feb 2025 14:11:48 +0100 Subject: [PATCH 14/25] Add more context to changelog entry --- packages/camera/camera_avfoundation/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_avfoundation/CHANGELOG.md b/packages/camera/camera_avfoundation/CHANGELOG.md index 6b2e63ff95e..d567ad63d06 100644 --- a/packages/camera/camera_avfoundation/CHANGELOG.md +++ b/packages/camera/camera_avfoundation/CHANGELOG.md @@ -1,6 +1,6 @@ ## 0.9.18+5 -* Migrates unit tests to Swift. +* Partially migrates unit tests to Swift. ## 0.9.18+4 From 2f7c9fd928de85264e086837adadfca2414ed63a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 11 Feb 2025 14:32:26 +0100 Subject: [PATCH 15/25] Refactor code according to style guide --- .../example/ios/RunnerTests/AvailableCamerasTests.swift | 9 +++++---- .../example/ios/RunnerTests/CameraExposureTests.swift | 3 ++- .../example/ios/RunnerTests/CameraFocusTests.swift | 3 ++- .../example/ios/RunnerTests/CameraPermissionTests.swift | 3 ++- .../ios/RunnerTests/CameraPreviewPauseTests.swift | 3 ++- .../example/ios/RunnerTests/CameraPropertiesTests.swift | 5 +++-- .../example/ios/RunnerTests/ExceptionCatcher.h | 2 +- .../example/ios/RunnerTests/ExceptionCatcher.m | 2 -- .../example/ios/RunnerTests/QueueUtilsTests.swift | 3 ++- 9 files changed, 19 insertions(+), 14 deletions(-) diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTests.swift index ca390d080ed..6c79392e474 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTests.swift @@ -4,10 +4,11 @@ import AVFoundation import XCTest -import camera_avfoundation + +@testable import camera_avfoundation final class AvailableCamerasTest: XCTestCase { - func createCameraPlugin(_ deviceDiscoverer: MockCameraDeviceDiscoverer) -> CameraPlugin { + func createCameraPlugin(with deviceDiscoverer: MockCameraDeviceDiscoverer) -> CameraPlugin { return CameraPlugin( registry: MockFlutterTextureRegistry(), messenger: MockFlutterBinaryMessenger(), @@ -21,7 +22,7 @@ final class AvailableCamerasTest: XCTestCase { func testAvailableCamerasShouldReturnAllCamerasOnMultiCameraIPhone() { let mockDeviceDiscoverer = MockCameraDeviceDiscoverer() - let cameraPlugin = createCameraPlugin(mockDeviceDiscoverer) + let cameraPlugin = createCameraPlugin(with: mockDeviceDiscoverer) let expectation = self.expectation(description: "Result finished") @@ -79,7 +80,7 @@ final class AvailableCamerasTest: XCTestCase { func testAvailableCamerasShouldReturnOneCameraOnSingleCameraIPhone() { let mockDeviceDiscoverer = MockCameraDeviceDiscoverer() - let cameraPlugin = createCameraPlugin(mockDeviceDiscoverer) + let cameraPlugin = createCameraPlugin(with: mockDeviceDiscoverer) let expectation = self.expectation(description: "Result finished") diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.swift index 055173252da..3a404dab930 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.swift @@ -4,7 +4,8 @@ import AVFoundation import XCTest -import camera_avfoundation + +@testable import camera_avfoundation final class CameraExposureTests: XCTestCase { var camera: FLTCam! diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.swift index 5dd34c3e8b0..b6377651783 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.swift @@ -4,7 +4,8 @@ import AVFoundation import XCTest -import camera_avfoundation + +@testable import camera_avfoundation final class CameraFocusTests: XCTestCase { var camera: FLTCam! diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.swift index ff11df4b049..872ece6dfc6 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.swift @@ -4,7 +4,8 @@ import AVFoundation import XCTest -import camera_avfoundation + +@testable import camera_avfoundation final class MockPermissionService: NSObject, FLTPermissionServicing { var authorizationStatusStub: ((AVMediaType) -> AVAuthorizationStatus)? diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.swift index 58849f3d507..1aca7656be0 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.swift @@ -4,7 +4,8 @@ import AVFoundation import XCTest -import camera_avfoundation + +@testable import camera_avfoundation final class CameraPreviewPauseTests: XCTestCase { private var camera: FLTCam! diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.swift index a408f1b74ee..86de424ea21 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.swift @@ -5,7 +5,8 @@ import AVFoundation import Foundation import XCTest -import camera_avfoundation + +@testable import camera_avfoundation final class CameraPropertiesTests: XCTestCase { // MARK: - Flash Mode Tests @@ -21,7 +22,7 @@ final class CameraPropertiesTests: XCTestCase { AVCaptureDevice.FlashMode.on, FCPGetAVCaptureFlashModeForPigeonFlashMode(FCPPlatformFlashMode.always)) - // TODO(FirentisTFW): Migrate implementation to throw Swift error in this case + // TODO(FirentisTFW): Migrate implementation to throw Swift error in this case. let exception = ExceptionCatcher.catchException { _ = FCPGetAVCaptureFlashModeForPigeonFlashMode(.torch) } diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/ExceptionCatcher.h b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ExceptionCatcher.h index 326b39e8d26..5e91835e841 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/ExceptionCatcher.h +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ExceptionCatcher.h @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// TODO(FirentisTFW): Remove this file when the plugin code using it is migrated to Swift. +// TODO(FirentisTFW): Remove this file when the plugin code that uses it is migrated to Swift. #import diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/ExceptionCatcher.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ExceptionCatcher.m index 914447786fc..b5b29c36a0c 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/ExceptionCatcher.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ExceptionCatcher.m @@ -5,7 +5,6 @@ #import "ExceptionCatcher.h" @implementation ExceptionCatcher - + (nullable NSException *)catchException:(void (^)(void))tryBlock { @try { tryBlock(); @@ -15,5 +14,4 @@ + (nullable NSException *)catchException:(void (^)(void))tryBlock { return exception; } } - @end diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/QueueUtilsTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/QueueUtilsTests.swift index de73eb62d64..1153f723999 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/QueueUtilsTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/QueueUtilsTests.swift @@ -3,7 +3,8 @@ // found in the LICENSE file. import XCTest -import camera_avfoundation + +@testable import camera_avfoundation final class QueueUtilsTests: XCTestCase { func testShouldStayOnMainQueueIfCalledFromMainQueue() { From 8701be9fd3e5c470733b74b98f01fdf23f22915d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 11 Feb 2025 14:35:07 +0100 Subject: [PATCH 16/25] Start changelog message with a verb --- packages/camera/camera_avfoundation/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_avfoundation/CHANGELOG.md b/packages/camera/camera_avfoundation/CHANGELOG.md index d567ad63d06..6b2e63ff95e 100644 --- a/packages/camera/camera_avfoundation/CHANGELOG.md +++ b/packages/camera/camera_avfoundation/CHANGELOG.md @@ -1,6 +1,6 @@ ## 0.9.18+5 -* Partially migrates unit tests to Swift. +* Migrates unit tests to Swift. ## 0.9.18+4 From 85ef5a198ea76f248e8a920503e954c626574924 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Wed, 12 Feb 2025 10:17:04 +0100 Subject: [PATCH 17/25] Make method private --- .../example/ios/RunnerTests/AvailableCamerasTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTests.swift index 6c79392e474..75fd503051a 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTests.swift @@ -8,7 +8,7 @@ import XCTest @testable import camera_avfoundation final class AvailableCamerasTest: XCTestCase { - func createCameraPlugin(with deviceDiscoverer: MockCameraDeviceDiscoverer) -> CameraPlugin { + private func createCameraPlugin(with deviceDiscoverer: MockCameraDeviceDiscoverer) -> CameraPlugin { return CameraPlugin( registry: MockFlutterTextureRegistry(), messenger: MockFlutterBinaryMessenger(), From 5ba37810388e19712664bbb8b099775eaaeb2b53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Wed, 12 Feb 2025 10:26:22 +0100 Subject: [PATCH 18/25] Compare CGPoints directly --- .../example/ios/RunnerTests/CameraExposureTests.swift | 3 +-- .../example/ios/RunnerTests/CameraFocusTests.swift | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.swift index 3a404dab930..9a1d3ce7142 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.swift @@ -43,8 +43,7 @@ final class CameraExposureTests: XCTestCase { } waitForExpectations(timeout: 30, handler: nil) - XCTAssertEqual(setPoint.x, 1.0) - XCTAssertEqual(setPoint.y, 1.0) + XCTAssertEqual(setPoint, CGPoint(x: 1.0, y: 1.0)) } func testSetExposurePoint_WhenNotSupported_ReturnsError() { diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.swift index b6377651783..30d5e959d2e 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.swift @@ -124,7 +124,7 @@ final class CameraFocusTests: XCTestCase { var setFocusPointOfInterestCalled = false mockDevice.setFocusPointOfInterestStub = { point in - if point.x == 1 && point.y == 1 { + if point == CGPoint(x: 1.0, y: 1.0) { setFocusPointOfInterestCalled = true } } From cacdf4cac42e395ad162ba8ee603f90b2e108d20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Wed, 12 Feb 2025 10:26:33 +0100 Subject: [PATCH 19/25] Make helper class fileprivate --- .../example/ios/RunnerTests/CameraPermissionTests.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.swift index 872ece6dfc6..0a573130699 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.swift @@ -7,7 +7,7 @@ import XCTest @testable import camera_avfoundation -final class MockPermissionService: NSObject, FLTPermissionServicing { +fileprivate final class MockPermissionService: NSObject, FLTPermissionServicing { var authorizationStatusStub: ((AVMediaType) -> AVAuthorizationStatus)? var requestAccessStub: ((AVMediaType, @escaping (Bool) -> Void) -> Void)? @@ -21,8 +21,8 @@ final class MockPermissionService: NSObject, FLTPermissionServicing { } final class FLTCameraPermissionManagerTests: XCTestCase { - var mockService: MockPermissionService! - var permissionManager: FLTCameraPermissionManager! + fileprivate var mockService: MockPermissionService! + private var permissionManager: FLTCameraPermissionManager! override func setUp() { mockService = MockPermissionService() From 7c589d1ebe4f3c15e6f851677e5325bc8f8a1397 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Wed, 12 Feb 2025 10:28:10 +0100 Subject: [PATCH 20/25] Do not use setUp for a file with only two tests --- .../example/ios/RunnerTests/CameraPreviewPauseTests.swift | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.swift index 1aca7656be0..378703a9ae4 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.swift @@ -8,19 +8,15 @@ import XCTest @testable import camera_avfoundation final class CameraPreviewPauseTests: XCTestCase { - private var camera: FLTCam! - - override func setUp() { - camera = FLTCam() - } - func testPausePreviewWithResult_shouldPausePreview() { + let camera = FLTCam() camera.pausePreview() XCTAssertTrue(camera.isPreviewPaused) } func testResumePreviewWithResult_shouldResumePreview() { + let camera = FLTCam() camera.resumePreview() XCTAssertFalse(camera.isPreviewPaused) From 605b4dff6709a85dc1214044ad4e4bf4a2bcc5d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Wed, 12 Feb 2025 11:11:46 +0100 Subject: [PATCH 21/25] Remove shared state from tests --- .../ios/RunnerTests/CameraExposureTests.swift | 20 +++--- .../ios/RunnerTests/CameraFocusTests.swift | 24 +++++--- .../RunnerTests/CameraPermissionTests.swift | 61 ++++++++++++------- 3 files changed, 66 insertions(+), 39 deletions(-) diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.swift index 9a1d3ce7142..cd8b167a6db 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.swift @@ -8,21 +8,21 @@ import XCTest @testable import camera_avfoundation final class CameraExposureTests: XCTestCase { - var camera: FLTCam! - var mockDevice: MockCaptureDevice! - var mockDeviceOrientationProvider: MockDeviceOrientationProvider! - - override func setUp() { - mockDevice = MockCaptureDevice() - mockDeviceOrientationProvider = MockDeviceOrientationProvider() + private func createSutAndMocks() -> (FLTCam, MockCaptureDevice, MockDeviceOrientationProvider) { + let mockDevice = MockCaptureDevice() + let mockDeviceOrientationProvider = MockDeviceOrientationProvider() let configuration = FLTCreateTestCameraConfiguration() - configuration.captureDeviceFactory = { self.mockDevice } + configuration.captureDeviceFactory = { mockDevice } configuration.deviceOrientationProvider = mockDeviceOrientationProvider - camera = FLTCreateCamWithConfiguration(configuration) + let camera = FLTCreateCamWithConfiguration(configuration) + + return (camera, mockDevice, mockDeviceOrientationProvider) } func testSetExposurePointWithResult_SetsExposurePointOfInterest() { + let (camera, mockDevice, mockDeviceOrientationProvider) = createSutAndMocks() + // UI is currently in landscape left orientation. mockDeviceOrientationProvider.orientation = .landscapeLeft // Exposure point of interest is supported. @@ -47,6 +47,8 @@ final class CameraExposureTests: XCTestCase { } func testSetExposurePoint_WhenNotSupported_ReturnsError() { + let (camera, mockDevice, mockDeviceOrientationProvider) = createSutAndMocks() + // UI is currently in landscape left orientation. mockDeviceOrientationProvider.orientation = .landscapeLeft // Exposure point of interest is not supported. diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.swift index 30d5e959d2e..afe61505a15 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.swift @@ -8,21 +8,21 @@ import XCTest @testable import camera_avfoundation final class CameraFocusTests: XCTestCase { - var camera: FLTCam! - var mockDevice: MockCaptureDevice! - var mockDeviceOrientationProvider: MockDeviceOrientationProvider! - - override func setUp() { - mockDevice = MockCaptureDevice() - mockDeviceOrientationProvider = MockDeviceOrientationProvider() + private func createSutAndMocks() -> (FLTCam, MockCaptureDevice, MockDeviceOrientationProvider) { + let mockDevice = MockCaptureDevice() + let mockDeviceOrientationProvider = MockDeviceOrientationProvider() let configuration = FLTCreateTestCameraConfiguration() - configuration.captureDeviceFactory = { self.mockDevice } + configuration.captureDeviceFactory = { mockDevice } configuration.deviceOrientationProvider = mockDeviceOrientationProvider - camera = FLTCreateCamWithConfiguration(configuration) + let camera = FLTCreateCamWithConfiguration(configuration) + + return (camera, mockDevice, mockDeviceOrientationProvider) } + func testAutoFocusWithContinuousModeSupported_ShouldSetContinuousAutoFocus() { + let (camera, mockDevice, _) = createSutAndMocks() // AVCaptureFocusModeContinuousAutoFocus and AVCaptureFocusModeAutoFocus are supported. mockDevice.isFocusModeSupportedStub = { mode in mode == .continuousAutoFocus || mode == .autoFocus @@ -45,6 +45,7 @@ final class CameraFocusTests: XCTestCase { } func testAutoFocusWithContinuousModeNotSupported_ShouldSetAutoFocus() { + let (camera, mockDevice, _) = createSutAndMocks() // AVCaptureFocusModeContinuousAutoFocus is not supported. // AVCaptureFocusModeAutoFocus is supported. mockDevice.isFocusModeSupportedStub = { mode in @@ -68,6 +69,7 @@ final class CameraFocusTests: XCTestCase { } func testAutoFocusWithNoModeSupported_ShouldSetNothing() { + let (camera, mockDevice, _) = createSutAndMocks() // No modes are supported. mockDevice.isFocusModeSupportedStub = { _ in false @@ -82,6 +84,7 @@ final class CameraFocusTests: XCTestCase { } func testLockedFocusWithModeSupported_ShouldSetModeAutoFocus() { + let (camera, mockDevice, _) = createSutAndMocks() // AVCaptureFocusModeContinuousAutoFocus and AVCaptureFocusModeAutoFocus are supported. mockDevice.isFocusModeSupportedStub = { mode in mode == .continuousAutoFocus || mode == .autoFocus @@ -104,6 +107,7 @@ final class CameraFocusTests: XCTestCase { } func testLockedFocusWithModeNotSupported_ShouldSetNothing() { + let (camera, mockDevice, _) = createSutAndMocks() mockDevice.isFocusModeSupportedStub = { mode in mode == .continuousAutoFocus } @@ -117,6 +121,7 @@ final class CameraFocusTests: XCTestCase { } func testSetFocusPointWithResult_SetsFocusPointOfInterest() { + let (camera, mockDevice, mockDeviceOrientationProvider) = createSutAndMocks() // UI is currently in landscape left orientation. mockDeviceOrientationProvider.orientation = .landscapeLeft // Focus point of interest is supported. @@ -137,6 +142,7 @@ final class CameraFocusTests: XCTestCase { } func testSetFocusPoint_WhenNotSupported_ReturnsError() { + let (camera, mockDevice, mockDeviceOrientationProvider) = createSutAndMocks() // UI is currently in landscape left orientation. mockDeviceOrientationProvider.orientation = .landscapeLeft // Focus point of interest is not supported. diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.swift index 0a573130699..87eaeea56d8 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.swift @@ -20,22 +20,23 @@ fileprivate final class MockPermissionService: NSObject, FLTPermissionServicing } } -final class FLTCameraPermissionManagerTests: XCTestCase { - fileprivate var mockService: MockPermissionService! - private var permissionManager: FLTCameraPermissionManager! - - override func setUp() { - mockService = MockPermissionService() - permissionManager = FLTCameraPermissionManager(permissionService: mockService) +final class CameraPermissionManagerTests: XCTestCase { + private func createSutAndMocks() -> (FLTCameraPermissionManager, MockPermissionService) { + let mockPermissionService = MockPermissionService() + let permissionManager = FLTCameraPermissionManager(permissionService: mockPermissionService) + + return (permissionManager, mockPermissionService) } // MARK: - Camera permissions func testRequestCameraPermission_completeWithoutErrorIfPreviouslyAuthorized() { + let (permissionManager, mockPermissionService) = createSutAndMocks() + let expectation = self.expectation( description: "Must complete without error if camera access was previously authorized.") - mockService.authorizationStatusStub = { mediaType in + mockPermissionService.authorizationStatusStub = { mediaType in XCTAssertEqual(mediaType, .video) return .authorized } @@ -49,6 +50,8 @@ final class FLTCameraPermissionManagerTests: XCTestCase { } func testRequestCameraPermission_completeWithErrorIfPreviouslyDenied() { + let (permissionManager, mockPermissionService) = createSutAndMocks() + let expectation = self.expectation( description: "Must complete with error if camera access was previously denied.") let expectedError = FlutterError( @@ -57,7 +60,7 @@ final class FLTCameraPermissionManagerTests: XCTestCase { "User has previously denied the camera access request. Go to Settings to enable camera access.", details: nil) - mockService.authorizationStatusStub = { mediaType in + mockPermissionService.authorizationStatusStub = { mediaType in XCTAssertEqual(mediaType, .video) return .denied } @@ -71,6 +74,8 @@ final class FLTCameraPermissionManagerTests: XCTestCase { } func testRequestCameraPermission_completeWithErrorIfRestricted() { + let (permissionManager, mockPermissionService) = createSutAndMocks() + let expectation = self.expectation( description: "Must complete with error if camera access is restricted.") let expectedError = FlutterError( @@ -78,7 +83,7 @@ final class FLTCameraPermissionManagerTests: XCTestCase { message: "Camera access is restricted.", details: nil) - mockService.authorizationStatusStub = { mediaType in + mockPermissionService.authorizationStatusStub = { mediaType in XCTAssertEqual(mediaType, .video) return .restricted } @@ -92,15 +97,17 @@ final class FLTCameraPermissionManagerTests: XCTestCase { } func testRequestCameraPermission_completeWithoutErrorIfUserGrantAccess() { + let (permissionManager, mockPermissionService) = createSutAndMocks() + let expectation = self.expectation( description: "Must complete without error if user granted access.") - mockService.authorizationStatusStub = { mediaType in + mockPermissionService.authorizationStatusStub = { mediaType in XCTAssertEqual(mediaType, .video) return .notDetermined } - mockService.requestAccessStub = { mediaType, handler in + mockPermissionService.requestAccessStub = { mediaType, handler in XCTAssertEqual(mediaType, .video) // Grant access. handler(true) @@ -115,6 +122,8 @@ final class FLTCameraPermissionManagerTests: XCTestCase { } func testRequestCameraPermission_completeWithErrorIfUserDenyAccess() { + let (permissionManager, mockPermissionService) = createSutAndMocks() + let expectation = self.expectation( description: "Must complete with error if user denied access.") let expectedError = FlutterError( @@ -122,12 +131,12 @@ final class FLTCameraPermissionManagerTests: XCTestCase { message: "User denied the camera access request.", details: nil) - mockService.authorizationStatusStub = { mediaType in + mockPermissionService.authorizationStatusStub = { mediaType in XCTAssertEqual(mediaType, .video) return .notDetermined } - mockService.requestAccessStub = { mediaType, handler in + mockPermissionService.requestAccessStub = { mediaType, handler in XCTAssertEqual(mediaType, .video) // Deny access. handler(false) @@ -144,10 +153,12 @@ final class FLTCameraPermissionManagerTests: XCTestCase { // MARK: - Audio permissions func testRequestAudioPermission_completeWithoutErrorIfPreviouslyAuthorized() { + let (permissionManager, mockPermissionService) = createSutAndMocks() + let expectation = self.expectation( description: "Must complete without error if audio access was previously authorized.") - mockService.authorizationStatusStub = { mediaType in + mockPermissionService.authorizationStatusStub = { mediaType in XCTAssertEqual(mediaType, .audio) return .authorized } @@ -161,6 +172,8 @@ final class FLTCameraPermissionManagerTests: XCTestCase { } func testRequestAudioPermission_completeWithErrorIfPreviouslyDenied() { + let (permissionManager, mockPermissionService) = createSutAndMocks() + let expectation = self.expectation( description: "Must complete with error if audio access was previously denied.") let expectedError = FlutterError( @@ -169,7 +182,7 @@ final class FLTCameraPermissionManagerTests: XCTestCase { "User has previously denied the audio access request. Go to Settings to enable audio access.", details: nil) - mockService.authorizationStatusStub = { mediaType in + mockPermissionService.authorizationStatusStub = { mediaType in XCTAssertEqual(mediaType, .audio) return .denied } @@ -183,6 +196,8 @@ final class FLTCameraPermissionManagerTests: XCTestCase { } func testRequestAudioPermission_completeWithErrorIfRestricted() { + let (permissionManager, mockPermissionService) = createSutAndMocks() + let expectation = self.expectation( description: "Must complete with error if audio access is restricted.") let expectedError = FlutterError( @@ -190,7 +205,7 @@ final class FLTCameraPermissionManagerTests: XCTestCase { message: "Audio access is restricted.", details: nil) - mockService.authorizationStatusStub = { mediaType in + mockPermissionService.authorizationStatusStub = { mediaType in XCTAssertEqual(mediaType, .audio) return .restricted } @@ -204,15 +219,17 @@ final class FLTCameraPermissionManagerTests: XCTestCase { } func testRequestAudioPermission_completeWithoutErrorIfUserGrantAccess() { + let (permissionManager, mockPermissionService) = createSutAndMocks() + let expectation = self.expectation( description: "Must complete without error if user granted access.") - mockService.authorizationStatusStub = { mediaType in + mockPermissionService.authorizationStatusStub = { mediaType in XCTAssertEqual(mediaType, .audio) return .notDetermined } - mockService.requestAccessStub = { mediaType, handler in + mockPermissionService.requestAccessStub = { mediaType, handler in XCTAssertEqual(mediaType, .audio) // Grant access. handler(true) @@ -227,6 +244,8 @@ final class FLTCameraPermissionManagerTests: XCTestCase { } func testRequestAudioPermission_completeWithErrorIfUserDenyAccess() { + let (permissionManager, mockPermissionService) = createSutAndMocks() + let expectation = self.expectation( description: "Must complete with error if user denied access") let expectedError = FlutterError( @@ -234,12 +253,12 @@ final class FLTCameraPermissionManagerTests: XCTestCase { message: "User denied the audio access request.", details: nil) - mockService.authorizationStatusStub = { mediaType in + mockPermissionService.authorizationStatusStub = { mediaType in XCTAssertEqual(mediaType, .audio) return .notDetermined } - mockService.requestAccessStub = { mediaType, handler in + mockPermissionService.requestAccessStub = { mediaType, handler in XCTAssertEqual(mediaType, .audio) // Deny access. handler(false) From 1453630c77813e7bd7936221884844c3f703fdaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Wed, 12 Feb 2025 11:34:31 +0100 Subject: [PATCH 22/25] Remove redundant empty lines --- .../RunnerTests/AvailableCamerasTests.swift | 4 ---- .../ios/RunnerTests/CameraExposureTests.swift | 2 -- .../RunnerTests/CameraPermissionTests.swift | 24 ------------------- .../RunnerTests/CameraPreviewPauseTests.swift | 2 ++ 4 files changed, 2 insertions(+), 30 deletions(-) diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTests.swift index 75fd503051a..996a38f1502 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTests.swift @@ -23,7 +23,6 @@ final class AvailableCamerasTest: XCTestCase { func testAvailableCamerasShouldReturnAllCamerasOnMultiCameraIPhone() { let mockDeviceDiscoverer = MockCameraDeviceDiscoverer() let cameraPlugin = createCameraPlugin(with: mockDeviceDiscoverer) - let expectation = self.expectation(description: "Result finished") // iPhone 13 Cameras: @@ -49,7 +48,6 @@ final class AvailableCamerasTest: XCTestCase { if #available(iOS 13.0, *) { requiredTypes.append(.builtInUltraWideCamera) } - var cameras: [MockCaptureDevice] = [wideAngleCamera, frontFacingCamera, telephotoCamera] if #available(iOS 13.0, *) { cameras.append(ultraWideCamera) @@ -81,7 +79,6 @@ final class AvailableCamerasTest: XCTestCase { func testAvailableCamerasShouldReturnOneCameraOnSingleCameraIPhone() { let mockDeviceDiscoverer = MockCameraDeviceDiscoverer() let cameraPlugin = createCameraPlugin(with: mockDeviceDiscoverer) - let expectation = self.expectation(description: "Result finished") // iPhone 8 Cameras: @@ -99,7 +96,6 @@ final class AvailableCamerasTest: XCTestCase { if #available(iOS 13.0, *) { requiredTypes.append(.builtInUltraWideCamera) } - let cameras: [MockCaptureDevice] = [wideAngleCamera, frontFacingCamera] mockDeviceDiscoverer.discoverySessionStub = { deviceTypes, mediaType, position in diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.swift index cd8b167a6db..b3d62d6b0aa 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.swift @@ -22,7 +22,6 @@ final class CameraExposureTests: XCTestCase { func testSetExposurePointWithResult_SetsExposurePointOfInterest() { let (camera, mockDevice, mockDeviceOrientationProvider) = createSutAndMocks() - // UI is currently in landscape left orientation. mockDeviceOrientationProvider.orientation = .landscapeLeft // Exposure point of interest is supported. @@ -48,7 +47,6 @@ final class CameraExposureTests: XCTestCase { func testSetExposurePoint_WhenNotSupported_ReturnsError() { let (camera, mockDevice, mockDeviceOrientationProvider) = createSutAndMocks() - // UI is currently in landscape left orientation. mockDeviceOrientationProvider.orientation = .landscapeLeft // Exposure point of interest is not supported. diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.swift index 87eaeea56d8..56dcab93844 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.swift @@ -32,7 +32,6 @@ final class CameraPermissionManagerTests: XCTestCase { func testRequestCameraPermission_completeWithoutErrorIfPreviouslyAuthorized() { let (permissionManager, mockPermissionService) = createSutAndMocks() - let expectation = self.expectation( description: "Must complete without error if camera access was previously authorized.") @@ -40,7 +39,6 @@ final class CameraPermissionManagerTests: XCTestCase { XCTAssertEqual(mediaType, .video) return .authorized } - permissionManager.requestCameraPermission { error in XCTAssertNil(error) expectation.fulfill() @@ -51,7 +49,6 @@ final class CameraPermissionManagerTests: XCTestCase { func testRequestCameraPermission_completeWithErrorIfPreviouslyDenied() { let (permissionManager, mockPermissionService) = createSutAndMocks() - let expectation = self.expectation( description: "Must complete with error if camera access was previously denied.") let expectedError = FlutterError( @@ -64,7 +61,6 @@ final class CameraPermissionManagerTests: XCTestCase { XCTAssertEqual(mediaType, .video) return .denied } - permissionManager.requestCameraPermission { error in XCTAssertEqual(error, expectedError) expectation.fulfill() @@ -75,7 +71,6 @@ final class CameraPermissionManagerTests: XCTestCase { func testRequestCameraPermission_completeWithErrorIfRestricted() { let (permissionManager, mockPermissionService) = createSutAndMocks() - let expectation = self.expectation( description: "Must complete with error if camera access is restricted.") let expectedError = FlutterError( @@ -87,7 +82,6 @@ final class CameraPermissionManagerTests: XCTestCase { XCTAssertEqual(mediaType, .video) return .restricted } - permissionManager.requestCameraPermission { error in XCTAssertEqual(error, expectedError) expectation.fulfill() @@ -98,7 +92,6 @@ final class CameraPermissionManagerTests: XCTestCase { func testRequestCameraPermission_completeWithoutErrorIfUserGrantAccess() { let (permissionManager, mockPermissionService) = createSutAndMocks() - let expectation = self.expectation( description: "Must complete without error if user granted access.") @@ -106,13 +99,11 @@ final class CameraPermissionManagerTests: XCTestCase { XCTAssertEqual(mediaType, .video) return .notDetermined } - mockPermissionService.requestAccessStub = { mediaType, handler in XCTAssertEqual(mediaType, .video) // Grant access. handler(true) } - permissionManager.requestCameraPermission { error in XCTAssertNil(error) expectation.fulfill() @@ -123,7 +114,6 @@ final class CameraPermissionManagerTests: XCTestCase { func testRequestCameraPermission_completeWithErrorIfUserDenyAccess() { let (permissionManager, mockPermissionService) = createSutAndMocks() - let expectation = self.expectation( description: "Must complete with error if user denied access.") let expectedError = FlutterError( @@ -135,13 +125,11 @@ final class CameraPermissionManagerTests: XCTestCase { XCTAssertEqual(mediaType, .video) return .notDetermined } - mockPermissionService.requestAccessStub = { mediaType, handler in XCTAssertEqual(mediaType, .video) // Deny access. handler(false) } - permissionManager.requestCameraPermission { error in XCTAssertEqual(error, expectedError) expectation.fulfill() @@ -154,7 +142,6 @@ final class CameraPermissionManagerTests: XCTestCase { func testRequestAudioPermission_completeWithoutErrorIfPreviouslyAuthorized() { let (permissionManager, mockPermissionService) = createSutAndMocks() - let expectation = self.expectation( description: "Must complete without error if audio access was previously authorized.") @@ -162,7 +149,6 @@ final class CameraPermissionManagerTests: XCTestCase { XCTAssertEqual(mediaType, .audio) return .authorized } - permissionManager.requestAudioPermission { error in XCTAssertNil(error) expectation.fulfill() @@ -173,7 +159,6 @@ final class CameraPermissionManagerTests: XCTestCase { func testRequestAudioPermission_completeWithErrorIfPreviouslyDenied() { let (permissionManager, mockPermissionService) = createSutAndMocks() - let expectation = self.expectation( description: "Must complete with error if audio access was previously denied.") let expectedError = FlutterError( @@ -186,7 +171,6 @@ final class CameraPermissionManagerTests: XCTestCase { XCTAssertEqual(mediaType, .audio) return .denied } - permissionManager.requestAudioPermission { error in XCTAssertEqual(error, expectedError) expectation.fulfill() @@ -197,7 +181,6 @@ final class CameraPermissionManagerTests: XCTestCase { func testRequestAudioPermission_completeWithErrorIfRestricted() { let (permissionManager, mockPermissionService) = createSutAndMocks() - let expectation = self.expectation( description: "Must complete with error if audio access is restricted.") let expectedError = FlutterError( @@ -209,7 +192,6 @@ final class CameraPermissionManagerTests: XCTestCase { XCTAssertEqual(mediaType, .audio) return .restricted } - permissionManager.requestAudioPermission { error in XCTAssertEqual(error, expectedError) expectation.fulfill() @@ -220,7 +202,6 @@ final class CameraPermissionManagerTests: XCTestCase { func testRequestAudioPermission_completeWithoutErrorIfUserGrantAccess() { let (permissionManager, mockPermissionService) = createSutAndMocks() - let expectation = self.expectation( description: "Must complete without error if user granted access.") @@ -228,13 +209,11 @@ final class CameraPermissionManagerTests: XCTestCase { XCTAssertEqual(mediaType, .audio) return .notDetermined } - mockPermissionService.requestAccessStub = { mediaType, handler in XCTAssertEqual(mediaType, .audio) // Grant access. handler(true) } - permissionManager.requestAudioPermission { error in XCTAssertNil(error) expectation.fulfill() @@ -245,7 +224,6 @@ final class CameraPermissionManagerTests: XCTestCase { func testRequestAudioPermission_completeWithErrorIfUserDenyAccess() { let (permissionManager, mockPermissionService) = createSutAndMocks() - let expectation = self.expectation( description: "Must complete with error if user denied access") let expectedError = FlutterError( @@ -257,13 +235,11 @@ final class CameraPermissionManagerTests: XCTestCase { XCTAssertEqual(mediaType, .audio) return .notDetermined } - mockPermissionService.requestAccessStub = { mediaType, handler in XCTAssertEqual(mediaType, .audio) // Deny access. handler(false) } - permissionManager.requestAudioPermission { error in XCTAssertEqual(error, expectedError) expectation.fulfill() diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.swift index 378703a9ae4..0c3759b6ee3 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.swift @@ -10,6 +10,7 @@ import XCTest final class CameraPreviewPauseTests: XCTestCase { func testPausePreviewWithResult_shouldPausePreview() { let camera = FLTCam() + camera.pausePreview() XCTAssertTrue(camera.isPreviewPaused) @@ -17,6 +18,7 @@ final class CameraPreviewPauseTests: XCTestCase { func testResumePreviewWithResult_shouldResumePreview() { let camera = FLTCam() + camera.resumePreview() XCTAssertFalse(camera.isPreviewPaused) From 4f9cbdd22dda4646260dd5e27c58282239f4773a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Wed, 12 Feb 2025 11:37:38 +0100 Subject: [PATCH 23/25] Add a todo to use errors when migrating to Swift --- .../Sources/camera_avfoundation/CameraProperties.m | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/CameraProperties.m b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/CameraProperties.m index 8ef61b43fc8..20e4d8a3ca9 100644 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/CameraProperties.m +++ b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/CameraProperties.m @@ -13,6 +13,7 @@ AVCaptureFlashMode FCPGetAVCaptureFlashModeForPigeonFlashMode(FCPPlatformFlashMo case FCPPlatformFlashModeAlways: return AVCaptureFlashModeOn; case FCPPlatformFlashModeTorch: + // TODO(FirentisTFW): Throw an error when migrating to Swift. NSCAssert(false, @"This mode cannot be converted, and requires custom handling."); return -1; } From a10c4ac614979d701d10819629ee3650ff3be808 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Wed, 12 Feb 2025 11:43:46 +0100 Subject: [PATCH 24/25] Format files --- .../example/ios/RunnerTests/AvailableCamerasTests.swift | 3 ++- .../example/ios/RunnerTests/CameraFocusTests.swift | 1 - .../example/ios/RunnerTests/CameraPermissionTests.swift | 4 ++-- .../example/ios/RunnerTests/CameraPreviewPauseTests.swift | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTests.swift index 996a38f1502..436ab08bf6d 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTests.swift @@ -8,7 +8,8 @@ import XCTest @testable import camera_avfoundation final class AvailableCamerasTest: XCTestCase { - private func createCameraPlugin(with deviceDiscoverer: MockCameraDeviceDiscoverer) -> CameraPlugin { + private func createCameraPlugin(with deviceDiscoverer: MockCameraDeviceDiscoverer) -> CameraPlugin + { return CameraPlugin( registry: MockFlutterTextureRegistry(), messenger: MockFlutterBinaryMessenger(), diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.swift index afe61505a15..b275a8bc9a6 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.swift @@ -20,7 +20,6 @@ final class CameraFocusTests: XCTestCase { return (camera, mockDevice, mockDeviceOrientationProvider) } - func testAutoFocusWithContinuousModeSupported_ShouldSetContinuousAutoFocus() { let (camera, mockDevice, _) = createSutAndMocks() // AVCaptureFocusModeContinuousAutoFocus and AVCaptureFocusModeAutoFocus are supported. diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.swift index 56dcab93844..511e6b6a152 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.swift @@ -7,7 +7,7 @@ import XCTest @testable import camera_avfoundation -fileprivate final class MockPermissionService: NSObject, FLTPermissionServicing { +private final class MockPermissionService: NSObject, FLTPermissionServicing { var authorizationStatusStub: ((AVMediaType) -> AVAuthorizationStatus)? var requestAccessStub: ((AVMediaType, @escaping (Bool) -> Void) -> Void)? @@ -24,7 +24,7 @@ final class CameraPermissionManagerTests: XCTestCase { private func createSutAndMocks() -> (FLTCameraPermissionManager, MockPermissionService) { let mockPermissionService = MockPermissionService() let permissionManager = FLTCameraPermissionManager(permissionService: mockPermissionService) - + return (permissionManager, mockPermissionService) } diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.swift index 0c3759b6ee3..e9934b93177 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.swift @@ -10,7 +10,7 @@ import XCTest final class CameraPreviewPauseTests: XCTestCase { func testPausePreviewWithResult_shouldPausePreview() { let camera = FLTCam() - + camera.pausePreview() XCTAssertTrue(camera.isPreviewPaused) @@ -18,7 +18,7 @@ final class CameraPreviewPauseTests: XCTestCase { func testResumePreviewWithResult_shouldResumePreview() { let camera = FLTCam() - + camera.resumePreview() XCTAssertFalse(camera.isPreviewPaused) From 20682dc108d9fac5c28bddebd18bd81244db86dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Wed, 12 Feb 2025 11:46:18 +0100 Subject: [PATCH 25/25] Add more information to a todo --- .../example/ios/RunnerTests/ExceptionCatcher.h | 2 ++ .../Sources/camera_avfoundation/CameraProperties.m | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/ExceptionCatcher.h b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ExceptionCatcher.h index 5e91835e841..ae5417d2c69 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/ExceptionCatcher.h +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ExceptionCatcher.h @@ -3,6 +3,8 @@ // found in the LICENSE file. // TODO(FirentisTFW): Remove this file when the plugin code that uses it is migrated to Swift. +// After the migration, the code should throw Swift errors instead of Objective-C exceptions, thus +// this file will not be needed. #import diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/CameraProperties.m b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/CameraProperties.m index 20e4d8a3ca9..8ef61b43fc8 100644 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/CameraProperties.m +++ b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/CameraProperties.m @@ -13,7 +13,6 @@ AVCaptureFlashMode FCPGetAVCaptureFlashModeForPigeonFlashMode(FCPPlatformFlashMo case FCPPlatformFlashModeAlways: return AVCaptureFlashModeOn; case FCPPlatformFlashModeTorch: - // TODO(FirentisTFW): Throw an error when migrating to Swift. NSCAssert(false, @"This mode cannot be converted, and requires custom handling."); return -1; }