Skip to content

Commit 3976c1b

Browse files
authored
[camera] Remove OCMock from permission tests (#8350)
Smaller part extracted from #8342 - Removes OCMock from `CameraPermissionTests` - Wraps permission methods into a new interface `FLTCameraPermissionManager` - Introduces new protocol which wraps framework methods and can be mocked directly `FLTPermissionService`
1 parent 29b2606 commit 3976c1b

File tree

10 files changed

+294
-185
lines changed

10 files changed

+294
-185
lines changed

packages/camera/camera_avfoundation/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
## NEXT
1+
## 0.9.17+6
22

33
* Updates minimum supported SDK version to Flutter 3.22/Dart 3.4.
4+
* Removes OCMock usage from permission tests
45

56
## 0.9.17+5
67

packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.m

Lines changed: 113 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -8,31 +8,58 @@
88
#endif
99
@import AVFoundation;
1010
@import XCTest;
11-
#import <OCMock/OCMock.h>
11+
1212
#import "CameraTestUtils.h"
1313

14-
@interface CameraPermissionTests : XCTestCase
14+
@interface MockPermissionService : NSObject <FLTPermissionServicing>
15+
@property(nonatomic, copy) AVAuthorizationStatus (^authorizationStatusStub)(AVMediaType mediaType);
16+
@property(nonatomic, copy) void (^requestAccessStub)(AVMediaType mediaType, void (^handler)(BOOL));
17+
@end
1518

19+
@implementation MockPermissionService
20+
- (AVAuthorizationStatus)authorizationStatusForMediaType:(AVMediaType)mediaType {
21+
return self.authorizationStatusStub ? self.authorizationStatusStub(mediaType)
22+
: AVAuthorizationStatusNotDetermined;
23+
}
24+
25+
- (void)requestAccessForMediaType:(AVMediaType)mediaType completionHandler:(void (^)(BOOL))handler {
26+
if (self.requestAccessStub) {
27+
self.requestAccessStub(mediaType, handler);
28+
}
29+
}
1630
@end
1731

18-
@implementation CameraPermissionTests
32+
@interface FLTCameraPermissionManagerTests : XCTestCase
33+
@property(nonatomic, strong) FLTCameraPermissionManager *permissionManager;
34+
@property(nonatomic, strong) MockPermissionService *mockService;
35+
@end
36+
37+
@implementation FLTCameraPermissionManagerTests
38+
39+
- (void)setUp {
40+
[super setUp];
41+
self.mockService = [[MockPermissionService alloc] init];
42+
self.permissionManager =
43+
[[FLTCameraPermissionManager alloc] initWithPermissionService:self.mockService];
44+
}
1945

2046
#pragma mark - camera permissions
2147

22-
- (void)testRequestCameraPermission_completeWithoutErrorIfPrevoiuslyAuthorized {
48+
- (void)testRequestCameraPermission_completeWithoutErrorIfPreviouslyAuthorized {
2349
XCTestExpectation *expectation =
2450
[self expectationWithDescription:
2551
@"Must copmlete without error if camera access was previously authorized."];
2652

27-
id mockDevice = OCMClassMock([AVCaptureDevice class]);
28-
OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeVideo])
29-
.andReturn(AVAuthorizationStatusAuthorized);
53+
self.mockService.authorizationStatusStub = ^AVAuthorizationStatus(AVMediaType mediaType) {
54+
XCTAssertEqualObjects(mediaType, AVMediaTypeVideo);
55+
return AVAuthorizationStatusAuthorized;
56+
};
3057

31-
FLTRequestCameraPermissionWithCompletionHandler(^(FlutterError *error) {
58+
[self.permissionManager requestCameraPermissionWithCompletionHandler:^(FlutterError *error) {
3259
if (error == nil) {
3360
[expectation fulfill];
3461
}
35-
});
62+
}];
3663
[self waitForExpectationsWithTimeout:1 handler:nil];
3764
}
3865
- (void)testRequestCameraPermission_completeWithErrorIfPreviouslyDenied {
@@ -45,14 +72,16 @@ - (void)testRequestCameraPermission_completeWithErrorIfPreviouslyDenied {
4572
@"Settings to enable camera access."
4673
details:nil];
4774

48-
id mockDevice = OCMClassMock([AVCaptureDevice class]);
49-
OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeVideo])
50-
.andReturn(AVAuthorizationStatusDenied);
51-
FLTRequestCameraPermissionWithCompletionHandler(^(FlutterError *error) {
75+
self.mockService.authorizationStatusStub = ^AVAuthorizationStatus(AVMediaType mediaType) {
76+
XCTAssertEqualObjects(mediaType, AVMediaTypeVideo);
77+
return AVAuthorizationStatusDenied;
78+
};
79+
80+
[self.permissionManager requestCameraPermissionWithCompletionHandler:^(FlutterError *error) {
5281
if ([error isEqual:expectedError]) {
5382
[expectation fulfill];
5483
}
55-
});
84+
}];
5685
[self waitForExpectationsWithTimeout:1 handler:nil];
5786
}
5887

@@ -63,37 +92,39 @@ - (void)testRequestCameraPermission_completeWithErrorIfRestricted {
6392
message:@"Camera access is restricted. "
6493
details:nil];
6594

66-
id mockDevice = OCMClassMock([AVCaptureDevice class]);
67-
OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeVideo])
68-
.andReturn(AVAuthorizationStatusRestricted);
95+
self.mockService.authorizationStatusStub = ^AVAuthorizationStatus(AVMediaType mediaType) {
96+
XCTAssertEqualObjects(mediaType, AVMediaTypeVideo);
97+
return AVAuthorizationStatusRestricted;
98+
};
6999

70-
FLTRequestCameraPermissionWithCompletionHandler(^(FlutterError *error) {
100+
[self.permissionManager requestCameraPermissionWithCompletionHandler:^(FlutterError *error) {
71101
if ([error isEqual:expectedError]) {
72102
[expectation fulfill];
73103
}
74-
});
104+
}];
75105
[self waitForExpectationsWithTimeout:1 handler:nil];
76106
}
77107

78108
- (void)testRequestCameraPermission_completeWithoutErrorIfUserGrantAccess {
79109
XCTestExpectation *grantedExpectation = [self
80110
expectationWithDescription:@"Must complete without error if user choose to grant access"];
81111

82-
id mockDevice = OCMClassMock([AVCaptureDevice class]);
83-
OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeVideo])
84-
.andReturn(AVAuthorizationStatusNotDetermined);
112+
self.mockService.authorizationStatusStub = ^AVAuthorizationStatus(AVMediaType mediaType) {
113+
XCTAssertEqualObjects(mediaType, AVMediaTypeVideo);
114+
return AVAuthorizationStatusNotDetermined;
115+
};
116+
85117
// Mimic user choosing "allow" in permission dialog.
86-
OCMStub([mockDevice requestAccessForMediaType:AVMediaTypeVideo
87-
completionHandler:[OCMArg checkWithBlock:^BOOL(void (^block)(BOOL)) {
88-
block(YES);
89-
return YES;
90-
}]]);
118+
self.mockService.requestAccessStub = ^(AVMediaType mediaType, void (^handler)(BOOL)) {
119+
XCTAssertEqualObjects(mediaType, AVMediaTypeVideo);
120+
handler(YES);
121+
};
91122

92-
FLTRequestCameraPermissionWithCompletionHandler(^(FlutterError *error) {
123+
[self.permissionManager requestCameraPermissionWithCompletionHandler:^(FlutterError *error) {
93124
if (error == nil) {
94125
[grantedExpectation fulfill];
95126
}
96-
});
127+
}];
97128
[self waitForExpectationsWithTimeout:1 handler:nil];
98129
}
99130

@@ -105,21 +136,22 @@ - (void)testRequestCameraPermission_completeWithErrorIfUserDenyAccess {
105136
message:@"User denied the camera access request."
106137
details:nil];
107138

108-
id mockDevice = OCMClassMock([AVCaptureDevice class]);
109-
OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeVideo])
110-
.andReturn(AVAuthorizationStatusNotDetermined);
139+
self.mockService.authorizationStatusStub = ^AVAuthorizationStatus(AVMediaType mediaType) {
140+
XCTAssertEqualObjects(mediaType, AVMediaTypeVideo);
141+
return AVAuthorizationStatusNotDetermined;
142+
};
111143

112144
// Mimic user choosing "deny" in permission dialog.
113-
OCMStub([mockDevice requestAccessForMediaType:AVMediaTypeVideo
114-
completionHandler:[OCMArg checkWithBlock:^BOOL(void (^block)(BOOL)) {
115-
block(NO);
116-
return YES;
117-
}]]);
118-
FLTRequestCameraPermissionWithCompletionHandler(^(FlutterError *error) {
145+
self.mockService.requestAccessStub = ^(AVMediaType mediaType, void (^handler)(BOOL)) {
146+
XCTAssertEqualObjects(mediaType, AVMediaTypeVideo);
147+
handler(NO);
148+
};
149+
150+
[self.permissionManager requestCameraPermissionWithCompletionHandler:^(FlutterError *error) {
119151
if ([error isEqual:expectedError]) {
120152
[expectation fulfill];
121153
}
122-
});
154+
}];
123155

124156
[self waitForExpectationsWithTimeout:1 handler:nil];
125157
}
@@ -131,17 +163,19 @@ - (void)testRequestAudioPermission_completeWithoutErrorIfPrevoiuslyAuthorized {
131163
[self expectationWithDescription:
132164
@"Must copmlete without error if audio access was previously authorized."];
133165

134-
id mockDevice = OCMClassMock([AVCaptureDevice class]);
135-
OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeAudio])
136-
.andReturn(AVAuthorizationStatusAuthorized);
166+
self.mockService.authorizationStatusStub = ^AVAuthorizationStatus(AVMediaType mediaType) {
167+
XCTAssertEqualObjects(mediaType, AVMediaTypeAudio);
168+
return AVAuthorizationStatusAuthorized;
169+
};
137170

138-
FLTRequestAudioPermissionWithCompletionHandler(^(FlutterError *error) {
171+
[self.permissionManager requestAudioPermissionWithCompletionHandler:^(FlutterError *error) {
139172
if (error == nil) {
140173
[expectation fulfill];
141174
}
142-
});
175+
}];
143176
[self waitForExpectationsWithTimeout:1 handler:nil];
144177
}
178+
145179
- (void)testRequestAudioPermission_completeWithErrorIfPreviouslyDenied {
146180
XCTestExpectation *expectation =
147181
[self expectationWithDescription:
@@ -152,14 +186,16 @@ - (void)testRequestAudioPermission_completeWithErrorIfPreviouslyDenied {
152186
@"Settings to enable audio access."
153187
details:nil];
154188

155-
id mockDevice = OCMClassMock([AVCaptureDevice class]);
156-
OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeAudio])
157-
.andReturn(AVAuthorizationStatusDenied);
158-
FLTRequestAudioPermissionWithCompletionHandler(^(FlutterError *error) {
189+
self.mockService.authorizationStatusStub = ^AVAuthorizationStatus(AVMediaType mediaType) {
190+
XCTAssertEqualObjects(mediaType, AVMediaTypeAudio);
191+
return AVAuthorizationStatusDenied;
192+
};
193+
194+
[self.permissionManager requestAudioPermissionWithCompletionHandler:^(FlutterError *error) {
159195
if ([error isEqual:expectedError]) {
160196
[expectation fulfill];
161197
}
162-
});
198+
}];
163199
[self waitForExpectationsWithTimeout:1 handler:nil];
164200
}
165201

@@ -170,37 +206,39 @@ - (void)testRequestAudioPermission_completeWithErrorIfRestricted {
170206
message:@"Audio access is restricted. "
171207
details:nil];
172208

173-
id mockDevice = OCMClassMock([AVCaptureDevice class]);
174-
OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeAudio])
175-
.andReturn(AVAuthorizationStatusRestricted);
209+
self.mockService.authorizationStatusStub = ^AVAuthorizationStatus(AVMediaType mediaType) {
210+
XCTAssertEqualObjects(mediaType, AVMediaTypeAudio);
211+
return AVAuthorizationStatusRestricted;
212+
};
176213

177-
FLTRequestAudioPermissionWithCompletionHandler(^(FlutterError *error) {
214+
[self.permissionManager requestAudioPermissionWithCompletionHandler:^(FlutterError *error) {
178215
if ([error isEqual:expectedError]) {
179216
[expectation fulfill];
180217
}
181-
});
218+
}];
182219
[self waitForExpectationsWithTimeout:1 handler:nil];
183220
}
184221

185222
- (void)testRequestAudioPermission_completeWithoutErrorIfUserGrantAccess {
186223
XCTestExpectation *grantedExpectation = [self
187224
expectationWithDescription:@"Must complete without error if user choose to grant access"];
188225

189-
id mockDevice = OCMClassMock([AVCaptureDevice class]);
190-
OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeAudio])
191-
.andReturn(AVAuthorizationStatusNotDetermined);
226+
self.mockService.authorizationStatusStub = ^AVAuthorizationStatus(AVMediaType mediaType) {
227+
XCTAssertEqualObjects(mediaType, AVMediaTypeAudio);
228+
return AVAuthorizationStatusNotDetermined;
229+
};
230+
192231
// Mimic user choosing "allow" in permission dialog.
193-
OCMStub([mockDevice requestAccessForMediaType:AVMediaTypeAudio
194-
completionHandler:[OCMArg checkWithBlock:^BOOL(void (^block)(BOOL)) {
195-
block(YES);
196-
return YES;
197-
}]]);
232+
self.mockService.requestAccessStub = ^(AVMediaType mediaType, void (^handler)(BOOL)) {
233+
XCTAssertEqualObjects(mediaType, AVMediaTypeAudio);
234+
handler(YES);
235+
};
198236

199-
FLTRequestAudioPermissionWithCompletionHandler(^(FlutterError *error) {
237+
[self.permissionManager requestAudioPermissionWithCompletionHandler:^(FlutterError *error) {
200238
if (error == nil) {
201239
[grantedExpectation fulfill];
202240
}
203-
});
241+
}];
204242
[self waitForExpectationsWithTimeout:1 handler:nil];
205243
}
206244

@@ -211,22 +249,22 @@ - (void)testRequestAudioPermission_completeWithErrorIfUserDenyAccess {
211249
message:@"User denied the audio access request."
212250
details:nil];
213251

214-
id mockDevice = OCMClassMock([AVCaptureDevice class]);
215-
OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeAudio])
216-
.andReturn(AVAuthorizationStatusNotDetermined);
252+
self.mockService.authorizationStatusStub = ^AVAuthorizationStatus(AVMediaType mediaType) {
253+
XCTAssertEqualObjects(mediaType, AVMediaTypeAudio);
254+
return AVAuthorizationStatusNotDetermined;
255+
};
217256

218257
// Mimic user choosing "deny" in permission dialog.
219-
OCMStub([mockDevice requestAccessForMediaType:AVMediaTypeAudio
220-
completionHandler:[OCMArg checkWithBlock:^BOOL(void (^block)(BOOL)) {
221-
block(NO);
222-
return YES;
223-
}]]);
224-
FLTRequestAudioPermissionWithCompletionHandler(^(FlutterError *error) {
258+
self.mockService.requestAccessStub = ^(AVMediaType mediaType, void (^handler)(BOOL)) {
259+
XCTAssertEqualObjects(mediaType, AVMediaTypeAudio);
260+
handler(NO);
261+
};
262+
263+
[self.permissionManager requestAudioPermissionWithCompletionHandler:^(FlutterError *error) {
225264
if ([error isEqual:expectedError]) {
226265
[expectation fulfill];
227266
}
228-
});
229-
267+
}];
230268
[self waitForExpectationsWithTimeout:1 handler:nil];
231269
}
232270

0 commit comments

Comments
 (0)