blob: 14491b221d291da57d76067c102452b04d4a420a [file] [log] [blame]
// 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 "ImagePickerTestImages.h"
@import image_picker_ios;
@import image_picker_ios.Test;
@import UniformTypeIdentifiers;
@import XCTest;
#import <OCMock/OCMock.h>
@interface MockViewController : UIViewController
@property(nonatomic, retain) UIViewController *mockPresented;
@end
@implementation MockViewController
@synthesize mockPresented;
- (UIViewController *)presentedViewController {
return mockPresented;
}
@end
@interface ImagePickerPluginTests : XCTestCase
@end
@implementation ImagePickerPluginTests
- (void)testPluginPickImageDeviceBack {
id mockUIImagePicker = OCMClassMock([UIImagePickerController class]);
id mockAVCaptureDevice = OCMClassMock([AVCaptureDevice class]);
// UIImagePickerControllerSourceTypeCamera is supported
OCMStub(ClassMethod(
[mockUIImagePicker isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]))
.andReturn(YES);
// UIImagePickerControllerCameraDeviceRear is supported
OCMStub(ClassMethod(
[mockUIImagePicker isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceRear]))
.andReturn(YES);
// AVAuthorizationStatusAuthorized is supported
OCMStub([mockAVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo])
.andReturn(AVAuthorizationStatusAuthorized);
// Run test
FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init];
UIImagePickerController *controller = [[UIImagePickerController alloc] init];
[plugin setImagePickerControllerOverrides:@[ controller ]];
[plugin pickImageWithSource:[FLTSourceSpecification makeWithType:FLTSourceTypeCamera
camera:FLTSourceCameraRear]
maxSize:[[FLTMaxSize alloc] init]
quality:nil
fullMetadata:@(YES)
completion:^(NSString *_Nullable result, FlutterError *_Nullable error){
}];
XCTAssertEqual(controller.cameraDevice, UIImagePickerControllerCameraDeviceRear);
}
- (void)testPluginPickImageDeviceFront {
id mockUIImagePicker = OCMClassMock([UIImagePickerController class]);
id mockAVCaptureDevice = OCMClassMock([AVCaptureDevice class]);
// UIImagePickerControllerSourceTypeCamera is supported
OCMStub(ClassMethod(
[mockUIImagePicker isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]))
.andReturn(YES);
// UIImagePickerControllerCameraDeviceFront is supported
OCMStub(ClassMethod(
[mockUIImagePicker isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceFront]))
.andReturn(YES);
// AVAuthorizationStatusAuthorized is supported
OCMStub([mockAVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo])
.andReturn(AVAuthorizationStatusAuthorized);
// Run test
FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init];
UIImagePickerController *controller = [[UIImagePickerController alloc] init];
[plugin setImagePickerControllerOverrides:@[ controller ]];
[plugin pickImageWithSource:[FLTSourceSpecification makeWithType:FLTSourceTypeCamera
camera:FLTSourceCameraFront]
maxSize:[[FLTMaxSize alloc] init]
quality:nil
fullMetadata:@(YES)
completion:^(NSString *_Nullable result, FlutterError *_Nullable error){
}];
XCTAssertEqual(controller.cameraDevice, UIImagePickerControllerCameraDeviceFront);
}
- (void)testPluginPickVideoDeviceBack {
id mockUIImagePicker = OCMClassMock([UIImagePickerController class]);
id mockAVCaptureDevice = OCMClassMock([AVCaptureDevice class]);
// UIImagePickerControllerSourceTypeCamera is supported
OCMStub(ClassMethod(
[mockUIImagePicker isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]))
.andReturn(YES);
// UIImagePickerControllerCameraDeviceRear is supported
OCMStub(ClassMethod(
[mockUIImagePicker isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceRear]))
.andReturn(YES);
// AVAuthorizationStatusAuthorized is supported
OCMStub([mockAVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo])
.andReturn(AVAuthorizationStatusAuthorized);
// Run test
FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init];
UIImagePickerController *controller = [[UIImagePickerController alloc] init];
[plugin setImagePickerControllerOverrides:@[ controller ]];
[plugin pickVideoWithSource:[FLTSourceSpecification makeWithType:FLTSourceTypeCamera
camera:FLTSourceCameraRear]
maxDuration:nil
completion:^(NSString *_Nullable result, FlutterError *_Nullable error){
}];
XCTAssertEqual(controller.cameraDevice, UIImagePickerControllerCameraDeviceRear);
}
- (void)testPluginPickVideoDeviceFront {
id mockUIImagePicker = OCMClassMock([UIImagePickerController class]);
id mockAVCaptureDevice = OCMClassMock([AVCaptureDevice class]);
// UIImagePickerControllerSourceTypeCamera is supported
OCMStub(ClassMethod(
[mockUIImagePicker isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]))
.andReturn(YES);
// UIImagePickerControllerCameraDeviceFront is supported
OCMStub(ClassMethod(
[mockUIImagePicker isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceFront]))
.andReturn(YES);
// AVAuthorizationStatusAuthorized is supported
OCMStub([mockAVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo])
.andReturn(AVAuthorizationStatusAuthorized);
// Run test
FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init];
UIImagePickerController *controller = [[UIImagePickerController alloc] init];
[plugin setImagePickerControllerOverrides:@[ controller ]];
[plugin pickVideoWithSource:[FLTSourceSpecification makeWithType:FLTSourceTypeCamera
camera:FLTSourceCameraFront]
maxDuration:nil
completion:^(NSString *_Nullable result, FlutterError *_Nullable error){
}];
XCTAssertEqual(controller.cameraDevice, UIImagePickerControllerCameraDeviceFront);
}
- (void)testPickMultiImageShouldUseUIImagePickerControllerOnPreiOS14 {
if (@available(iOS 14, *)) {
return;
}
id mockUIImagePicker = OCMClassMock([UIImagePickerController class]);
id photoLibrary = OCMClassMock([PHPhotoLibrary class]);
OCMStub(ClassMethod([photoLibrary authorizationStatus]))
.andReturn(PHAuthorizationStatusAuthorized);
FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init];
[plugin setImagePickerControllerOverrides:@[ mockUIImagePicker ]];
[plugin pickMultiImageWithMaxSize:[FLTMaxSize makeWithWidth:@(100) height:@(200)]
quality:@(50)
fullMetadata:@(YES)
completion:^(NSArray<NSString *> *_Nullable result,
FlutterError *_Nullable error){
}];
OCMVerify(times(1),
[mockUIImagePicker setSourceType:UIImagePickerControllerSourceTypePhotoLibrary]);
}
- (void)testPickImageWithoutFullMetadata API_AVAILABLE(ios(11)) {
id mockUIImagePicker = OCMClassMock([UIImagePickerController class]);
id photoLibrary = OCMClassMock([PHPhotoLibrary class]);
FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init];
[plugin setImagePickerControllerOverrides:@[ mockUIImagePicker ]];
[plugin pickImageWithSource:[FLTSourceSpecification makeWithType:FLTSourceTypeGallery
camera:FLTSourceCameraFront]
maxSize:[[FLTMaxSize alloc] init]
quality:nil
fullMetadata:@(NO)
completion:^(NSString *_Nullable result, FlutterError *_Nullable error){
}];
OCMVerify(times(0), [photoLibrary authorizationStatus]);
}
- (void)testPickMultiImageWithoutFullMetadata API_AVAILABLE(ios(11)) {
id mockUIImagePicker = OCMClassMock([UIImagePickerController class]);
id photoLibrary = OCMClassMock([PHPhotoLibrary class]);
FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init];
[plugin setImagePickerControllerOverrides:@[ mockUIImagePicker ]];
[plugin pickMultiImageWithMaxSize:[[FLTMaxSize alloc] init]
quality:nil
fullMetadata:@(NO)
completion:^(NSArray<NSString *> *_Nullable result,
FlutterError *_Nullable error){
}];
OCMVerify(times(0), [photoLibrary authorizationStatus]);
}
#pragma mark - Test camera devices, no op on simulators
- (void)testPluginPickImageDeviceCancelClickMultipleTimes {
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
return;
}
FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init];
UIImagePickerController *controller = [[UIImagePickerController alloc] init];
plugin.imagePickerControllerOverrides = @[ controller ];
[plugin pickImageWithSource:[FLTSourceSpecification makeWithType:FLTSourceTypeCamera
camera:FLTSourceCameraRear]
maxSize:[[FLTMaxSize alloc] init]
quality:nil
fullMetadata:@(YES)
completion:^(NSString *_Nullable result, FlutterError *_Nullable error){
}];
// To ensure the flow does not crash by multiple cancel call
[plugin imagePickerControllerDidCancel:controller];
[plugin imagePickerControllerDidCancel:controller];
}
#pragma mark - Test video duration
- (void)testPickingVideoWithDuration {
FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init];
UIImagePickerController *controller = [[UIImagePickerController alloc] init];
[plugin setImagePickerControllerOverrides:@[ controller ]];
[plugin pickVideoWithSource:[FLTSourceSpecification makeWithType:FLTSourceTypeCamera
camera:FLTSourceCameraRear]
maxDuration:@(95)
completion:^(NSString *_Nullable result, FlutterError *_Nullable error){
}];
XCTAssertEqual(controller.videoMaximumDuration, 95);
}
- (void)testViewController {
UIWindow *window = [UIWindow new];
MockViewController *vc1 = [MockViewController new];
window.rootViewController = vc1;
UIViewController *vc2 = [UIViewController new];
vc1.mockPresented = vc2;
FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init];
XCTAssertEqual([plugin viewControllerWithWindow:window], vc2);
}
- (void)testPluginMultiImagePathHasNullItem {
FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init];
XCTestExpectation *resultExpectation = [self expectationWithDescription:@"result"];
plugin.callContext = [[FLTImagePickerMethodCallContext alloc]
initWithResult:^(NSArray<NSString *> *_Nullable result, FlutterError *_Nullable error) {
XCTAssertEqualObjects(error.code, @"create_error");
[resultExpectation fulfill];
}];
[plugin sendCallResultWithSavedPathList:@[ [NSNull null] ]];
[self waitForExpectationsWithTimeout:30 handler:nil];
}
- (void)testPluginMultiImagePathHasItem {
FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init];
NSArray *pathList = @[ @"test" ];
XCTestExpectation *resultExpectation = [self expectationWithDescription:@"result"];
plugin.callContext = [[FLTImagePickerMethodCallContext alloc]
initWithResult:^(NSArray<NSString *> *_Nullable result, FlutterError *_Nullable error) {
XCTAssertEqualObjects(result, pathList);
[resultExpectation fulfill];
}];
[plugin sendCallResultWithSavedPathList:pathList];
[self waitForExpectationsWithTimeout:30 handler:nil];
}
- (void)testSendsImageInvalidSourceError API_AVAILABLE(ios(14)) {
id mockPickerViewController = OCMClassMock([PHPickerViewController class]);
id mockItemProvider = OCMClassMock([NSItemProvider class]);
// Does not conform to image, invalid source.
OCMStub([mockItemProvider hasItemConformingToTypeIdentifier:OCMOCK_ANY]).andReturn(NO);
PHPickerResult *failResult1 = OCMClassMock([PHPickerResult class]);
OCMStub([failResult1 itemProvider]).andReturn(mockItemProvider);
PHPickerResult *failResult2 = OCMClassMock([PHPickerResult class]);
OCMStub([failResult2 itemProvider]).andReturn(mockItemProvider);
FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init];
XCTestExpectation *resultExpectation = [self expectationWithDescription:@"result"];
plugin.callContext = [[FLTImagePickerMethodCallContext alloc]
initWithResult:^(NSArray<NSString *> *result, FlutterError *error) {
XCTAssertTrue(NSThread.isMainThread);
XCTAssertNil(result);
XCTAssertEqualObjects(error.code, @"invalid_source");
[resultExpectation fulfill];
}];
[plugin picker:mockPickerViewController didFinishPicking:@[ failResult1, failResult2 ]];
[self waitForExpectationsWithTimeout:30 handler:nil];
}
- (void)testSendsImageInvalidErrorWhenOneFails API_AVAILABLE(ios(14)) {
id mockPickerViewController = OCMClassMock([PHPickerViewController class]);
NSError *loadDataError = [NSError errorWithDomain:@"PHPickerDomain" code:1234 userInfo:nil];
id mockFailItemProvider = OCMClassMock([NSItemProvider class]);
OCMStub([mockFailItemProvider hasItemConformingToTypeIdentifier:OCMOCK_ANY]).andReturn(YES);
[[mockFailItemProvider stub]
loadDataRepresentationForTypeIdentifier:OCMOCK_ANY
completionHandler:[OCMArg invokeBlockWithArgs:[NSNull null],
loadDataError, nil]];
PHPickerResult *failResult = OCMClassMock([PHPickerResult class]);
OCMStub([failResult itemProvider]).andReturn(mockFailItemProvider);
NSURL *tiffURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"tiffImage"
withExtension:@"tiff"];
NSItemProvider *tiffItemProvider = [[NSItemProvider alloc] initWithContentsOfURL:tiffURL];
PHPickerResult *tiffResult = OCMClassMock([PHPickerResult class]);
OCMStub([tiffResult itemProvider]).andReturn(tiffItemProvider);
FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init];
XCTestExpectation *resultExpectation = [self expectationWithDescription:@"result"];
plugin.callContext = [[FLTImagePickerMethodCallContext alloc]
initWithResult:^(NSArray<NSString *> *result, FlutterError *error) {
XCTAssertTrue(NSThread.isMainThread);
XCTAssertNil(result);
XCTAssertEqualObjects(error.code, @"invalid_image");
[resultExpectation fulfill];
}];
[plugin picker:mockPickerViewController didFinishPicking:@[ failResult, tiffResult ]];
[self waitForExpectationsWithTimeout:30 handler:nil];
}
- (void)testSavesImages API_AVAILABLE(ios(14)) {
id mockPickerViewController = OCMClassMock([PHPickerViewController class]);
NSURL *tiffURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"tiffImage"
withExtension:@"tiff"];
NSItemProvider *tiffItemProvider = [[NSItemProvider alloc] initWithContentsOfURL:tiffURL];
PHPickerResult *tiffResult = OCMClassMock([PHPickerResult class]);
OCMStub([tiffResult itemProvider]).andReturn(tiffItemProvider);
NSURL *pngURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"pngImage"
withExtension:@"png"];
NSItemProvider *pngItemProvider = [[NSItemProvider alloc] initWithContentsOfURL:pngURL];
PHPickerResult *pngResult = OCMClassMock([PHPickerResult class]);
OCMStub([pngResult itemProvider]).andReturn(pngItemProvider);
FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init];
XCTestExpectation *resultExpectation = [self expectationWithDescription:@"result"];
plugin.callContext = [[FLTImagePickerMethodCallContext alloc]
initWithResult:^(NSArray<NSString *> *result, FlutterError *error) {
XCTAssertTrue(NSThread.isMainThread);
XCTAssertEqual(result.count, 2);
XCTAssertNil(error);
[resultExpectation fulfill];
}];
[plugin picker:mockPickerViewController didFinishPicking:@[ tiffResult, pngResult ]];
[self waitForExpectationsWithTimeout:30 handler:nil];
}
@end