Skip to content

Commit

Permalink
[camera] Remove OCMock from CameraExposureTests and CameraFocusTests (#…
Browse files Browse the repository at this point in the history
…8351)

Extracted from #8342 to keep PRs smaller

- Introduces `FLTDeviceOrientationProviding` and `FLTCaptureDeviceControlling` protocols
- Removes `OCMock.h` reference from `CameraExposureTests.h` and `CameraFocusTests.h`
  • Loading branch information
mchudy authored Jan 24, 2025
1 parent 3d28a90 commit 73857c6
Show file tree
Hide file tree
Showing 25 changed files with 857 additions and 148 deletions.
4 changes: 4 additions & 0 deletions packages/camera/camera_avfoundation/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.9.18+1

* Refactors implementations to reduce usage of OCMock in internal testing.

## 0.9.18

* Adds API support query for image streaming.
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -5,51 +5,79 @@
@import camera_avfoundation;
@import XCTest;
@import AVFoundation;
#import <OCMock/OCMock.h>

@interface FLTCam : NSObject <FlutterTexture,
AVCaptureVideoDataOutputSampleBufferDelegate,
AVCaptureAudioDataOutputSampleBufferDelegate>

- (void)setExposurePointWithResult:(FlutterResult)result x:(double)x y:(double)y;
@end
#import "CameraTestUtils.h"
#import "MockCaptureDeviceController.h"
#import "MockDeviceOrientationProvider.h"

@interface CameraExposureTests : XCTestCase
@property(readonly, nonatomic) FLTCam *camera;
@property(readonly, nonatomic) id mockDevice;
@property(readonly, nonatomic) id mockUIDevice;
@property(readonly, nonatomic) MockCaptureDeviceController *mockDevice;
@property(readonly, nonatomic) MockDeviceOrientationProvider *mockDeviceOrientationProvider;
@end

@implementation CameraExposureTests

- (void)setUp {
_camera = [[FLTCam alloc] init];
_mockDevice = OCMClassMock([AVCaptureDevice class]);
_mockUIDevice = OCMPartialMock([UIDevice currentDevice]);
}
MockCaptureDeviceController *mockDevice = [[MockCaptureDeviceController alloc] init];
_mockDeviceOrientationProvider = [[MockDeviceOrientationProvider alloc] init];
_mockDevice = mockDevice;

- (void)tearDown {
[_mockDevice stopMocking];
[_mockUIDevice stopMocking];
_camera = FLTCreateCamWithCaptureSessionQueueAndMediaSettings(
nil, nil, nil,
^id<FLTCaptureDeviceControlling>(void) {
return mockDevice;
},
_mockDeviceOrientationProvider);
}

- (void)testSetExpsourePointWithResult_SetsExposurePointOfInterest {
- (void)testSetExposurePointWithResult_SetsExposurePointOfInterest {
// UI is currently in landscape left orientation
OCMStub([(UIDevice *)_mockUIDevice orientation]).andReturn(UIDeviceOrientationLandscapeLeft);
_mockDeviceOrientationProvider.orientation = UIDeviceOrientationLandscapeLeft;
// Exposure point of interest is supported
OCMStub([_mockDevice isExposurePointOfInterestSupported]).andReturn(true);
// Set mock device as the current capture device
[_camera setValue:_mockDevice forKey:@"captureDevice"];
_mockDevice.exposurePointOfInterestSupported = YES;

// Verify the focus point of interest has been set
__block CGPoint setPoint = CGPointZero;
_mockDevice.setExposurePointOfInterestStub = ^(CGPoint point) {
if (CGPointEqualToPoint(CGPointMake(1, 1), point)) {
setPoint = point;
}
};

// Run test
XCTestExpectation *completionExpectation = [self expectationWithDescription:@"Completion called"];
[_camera setExposurePoint:[FCPPlatformPoint makeWithX:1 y:1]
withCompletion:^(FlutterError *_Nullable error) {
XCTAssertNil(error);
[completionExpectation fulfill];
}];

[self waitForExpectationsWithTimeout:1 handler:nil];
XCTAssertEqual(setPoint.x, 1.0);
XCTAssertEqual(setPoint.y, 1.0);
}

- (void)testSetExposurePoint_WhenNotSupported_ReturnsError {
// UI is currently in landscape left orientation
_mockDeviceOrientationProvider.orientation = UIDeviceOrientationLandscapeLeft;
// Exposure point of interest is not supported
_mockDevice.exposurePointOfInterestSupported = NO;

XCTestExpectation *expectation = [self expectationWithDescription:@"Completion with error"];

// Run
[_camera
setExposurePointWithResult:^void(id _Nullable result) {
}
x:1
y:1];
setExposurePoint:[FCPPlatformPoint makeWithX:1 y:1]
withCompletion:^(FlutterError *_Nullable error) {
XCTAssertNotNil(error);
XCTAssertEqualObjects(error.code, @"setExposurePointFailed");
XCTAssertEqualObjects(error.message, @"Device does not have exposure point capabilities");
[expectation fulfill];
}];

// Verify the focus point of interest has been set
OCMVerify([_mockDevice setExposurePointOfInterest:CGPointMake(1, 1)]);
// Verify
[self waitForExpectationsWithTimeout:1 handler:nil];
}

@end
Original file line number Diff line number Diff line change
Expand Up @@ -8,120 +8,174 @@
#endif
@import XCTest;
@import AVFoundation;
#import <OCMock/OCMock.h>

#import "CameraTestUtils.h"
#import "MockCaptureDeviceController.h"
#import "MockDeviceOrientationProvider.h"

@interface CameraFocusTests : XCTestCase
@property(readonly, nonatomic) FLTCam *camera;
@property(readonly, nonatomic) id mockDevice;
@property(readonly, nonatomic) id mockUIDevice;
@property(readonly, nonatomic) MockCaptureDeviceController *mockDevice;
@property(readonly, nonatomic) MockDeviceOrientationProvider *mockDeviceOrientationProvider;
@end

@implementation CameraFocusTests

- (void)setUp {
_camera = [[FLTCam alloc] init];
_mockDevice = OCMClassMock([AVCaptureDevice class]);
_mockUIDevice = OCMPartialMock([UIDevice currentDevice]);
}

- (void)tearDown {
[_mockDevice stopMocking];
[_mockUIDevice stopMocking];
MockCaptureDeviceController *mockDevice = [[MockCaptureDeviceController alloc] init];
_mockDevice = mockDevice;
_mockDeviceOrientationProvider = [[MockDeviceOrientationProvider alloc] init];

_camera = FLTCreateCamWithCaptureSessionQueueAndMediaSettings(
nil, nil, nil,
^id<FLTCaptureDeviceControlling>(void) {
return mockDevice;
},
_mockDeviceOrientationProvider);
}

- (void)testAutoFocusWithContinuousModeSupported_ShouldSetContinuousAutoFocus {
// AVCaptureFocusModeContinuousAutoFocus is supported
OCMStub([_mockDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]).andReturn(true);
// AVCaptureFocusModeContinuousAutoFocus is supported
OCMStub([_mockDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]).andReturn(true);

// Don't expect setFocusMode:AVCaptureFocusModeAutoFocus
[[_mockDevice reject] setFocusMode:AVCaptureFocusModeAutoFocus];
// 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
OCMVerify([_mockDevice setFocusMode:AVCaptureFocusModeContinuousAutoFocus]);
XCTAssertTrue(setFocusModeContinuousAutoFocusCalled);
}

- (void)testAutoFocusWithContinuousModeNotSupported_ShouldSetAutoFocus {
// AVCaptureFocusModeContinuousAutoFocus is not supported
OCMStub([_mockDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus])
.andReturn(false);
// AVCaptureFocusModeContinuousAutoFocus is supported
OCMStub([_mockDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]).andReturn(true);
// AVCaptureFocusModeAutoFocus is supported
_mockDevice.isFocusModeSupportedStub = ^BOOL(AVCaptureFocusMode mode) {
return mode == AVCaptureFocusModeAutoFocus;
};

__block BOOL setFocusModeAutoFocusCalled = NO;

// Don't expect setFocusMode:AVCaptureFocusModeContinuousAutoFocus
[[_mockDevice reject] 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
OCMVerify([_mockDevice setFocusMode:AVCaptureFocusModeAutoFocus]);
XCTAssertTrue(setFocusModeAutoFocusCalled);
}

- (void)testAutoFocusWithNoModeSupported_ShouldSetNothing {
// AVCaptureFocusModeContinuousAutoFocus is not supported
OCMStub([_mockDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus])
.andReturn(false);
// AVCaptureFocusModeContinuousAutoFocus is not supported
OCMStub([_mockDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]).andReturn(false);
// No modes are supported
_mockDevice.isFocusModeSupportedStub = ^BOOL(AVCaptureFocusMode mode) {
return NO;
};

// Don't expect any setFocus
[[_mockDevice reject] setFocusMode:AVCaptureFocusModeContinuousAutoFocus];
[[_mockDevice reject] setFocusMode:AVCaptureFocusModeAutoFocus];
_mockDevice.setFocusModeStub = ^(AVCaptureFocusMode mode) {
XCTFail(@"Unexpected call to setFocusMode");
};

// Run test
[_camera applyFocusMode:FCPPlatformFocusModeAuto onDevice:_mockDevice];
}

- (void)testLockedFocusWithModeSupported_ShouldSetModeAutoFocus {
// AVCaptureFocusModeContinuousAutoFocus is supported
OCMStub([_mockDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]).andReturn(true);
// AVCaptureFocusModeContinuousAutoFocus is supported
OCMStub([_mockDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]).andReturn(true);

// Don't expect any setFocus
[[_mockDevice reject] setFocusMode:AVCaptureFocusModeContinuousAutoFocus];
// 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];

// Expect setFocusMode:AVCaptureFocusModeAutoFocus
OCMVerify([_mockDevice setFocusMode:AVCaptureFocusModeAutoFocus]);
XCTAssertTrue(setFocusModeAutoFocusCalled);
}

- (void)testLockedFocusWithModeNotSupported_ShouldSetNothing {
// AVCaptureFocusModeContinuousAutoFocus is supported
OCMStub([_mockDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]).andReturn(true);
// AVCaptureFocusModeContinuousAutoFocus is not supported
OCMStub([_mockDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]).andReturn(false);
_mockDevice.isFocusModeSupportedStub = ^BOOL(AVCaptureFocusMode mode) {
return mode == AVCaptureFocusModeContinuousAutoFocus;
};

// Don't expect any setFocus
[[_mockDevice reject] setFocusMode:AVCaptureFocusModeContinuousAutoFocus];
[[_mockDevice reject] setFocusMode:AVCaptureFocusModeAutoFocus];
_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
OCMStub([(UIDevice *)_mockUIDevice orientation]).andReturn(UIDeviceOrientationLandscapeLeft);
_mockDeviceOrientationProvider.orientation = UIDeviceOrientationLandscapeLeft;
// Focus point of interest is supported
OCMStub([_mockDevice isFocusPointOfInterestSupported]).andReturn(true);
// Set mock device as the current capture device
[_camera setValue:_mockDevice forKey:@"captureDevice"];
_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
OCMVerify([_mockDevice setFocusPointOfInterest:CGPointMake(1, 1)]);
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:1 handler:nil];
}

@end
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

#import <OCMock/OCMock.h>

#import "MockCaptureDeviceController.h"

@interface StubGlobalEventApi : FCPCameraGlobalEventApi
@property(nonatomic) BOOL called;
@property(nonatomic) FCPPlatformDeviceOrientation lastOrientation;
Expand All @@ -35,10 +37,21 @@ - (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(nonnull NSString
#pragma mark -

@interface CameraOrientationTests : XCTestCase
@property(readonly, nonatomic) FLTCam *camera;
@property(readonly, nonatomic) MockCaptureDeviceController *mockDevice;
@property(readonly, nonatomic) StubGlobalEventApi *eventAPI;
@end

@implementation CameraOrientationTests

- (void)setUp {
[super setUp];
_mockDevice = [[MockCaptureDeviceController alloc] init];
_camera = [[FLTCam alloc] init];

[_camera setValue:_mockDevice forKey:@"captureDevice"];
}

// Ensure that the given queue and then the main queue have both cycled, to wait for any pending
// async events that may have been bounced between them.
- (void)waitForRoundTripWithQueue:(dispatch_queue_t)queue {
Expand Down Expand Up @@ -97,13 +110,13 @@ - (void)testOrientationNotificationsNotCalledForFaceDown {
- (void)testOrientationUpdateMustBeOnCaptureSessionQueue {
XCTestExpectation *queueExpectation = [self
expectationWithDescription:@"Orientation update must happen on the capture session queue"];

CameraPlugin *camera = [[CameraPlugin alloc] initWithRegistry:nil messenger:nil];
const char *captureSessionQueueSpecific = "capture_session_queue";
dispatch_queue_set_specific(camera.captureSessionQueue, captureSessionQueueSpecific,
(void *)captureSessionQueueSpecific, NULL);
FLTCam *mockCam = OCMClassMock([FLTCam class]);
camera.camera = mockCam;

OCMStub([mockCam setDeviceOrientation:UIDeviceOrientationLandscapeLeft])
.andDo(^(NSInvocation *invocation) {
if (dispatch_get_specific(captureSessionQueueSpecific)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
#endif
@import XCTest;
@import AVFoundation;
#import <OCMock/OCMock.h>

@interface CameraPreviewPauseTests : XCTestCase
@end
Expand Down
Loading

0 comments on commit 73857c6

Please sign in to comment.