Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit 2b5555a

Browse files
committed
Merged in unit test refactoring
2 parents 9d15c59 + 3921f8d commit 2b5555a

File tree

12 files changed

+192
-21
lines changed

12 files changed

+192
-21
lines changed

packages/image_picker/image_picker/CHANGELOG.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1-
## 0.8.4+6
1+
## 0.8.4+7
22

33
* Ensures the `result` callback and method call `arguments` send to iOS are
44
taken into consideration when picking multiple images on pre-iOS 14 devices;
5-
* Configures the `UIImagePicker` to default to gallery instead of camera on
6-
pre-iOS 14 devices when picking multiple images on pre-iOS 14 devices.
5+
* Configures the `UIImagePicker` to default to gallery instead of camera when
6+
picking multiple images on pre-iOS 14 devices.
7+
8+
## 0.8.4+6
9+
10+
* Refactors unit test to expose private interface via a separate test header instead of the inline declaration.
711

812
## 0.8.4+5
913

packages/image_picker/image_picker/example/ios/RunnerTests/ImagePickerPluginTests.m

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#import "ImagePickerTestImages.h"
66

77
@import image_picker;
8+
@import image_picker.Test;
89
@import XCTest;
910
#import <OCMock/OCMock.h>
1011

@@ -21,13 +22,6 @@ - (UIViewController *)presentedViewController {
2122

2223
@end
2324

24-
@interface FLTImagePickerPlugin (Test)
25-
@property(copy, nonatomic) FlutterResult result;
26-
@property(copy, nonatomic) NSDictionary *arguments;
27-
- (void)handleSavedPathList:(NSMutableArray *)pathList;
28-
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker;
29-
@end
30-
3125
@interface ImagePickerPluginTests : XCTestCase
3226
@property(readonly, nonatomic) id mockUIImagePicker;
3327
@property(readonly, nonatomic) id mockAVCaptureDevice;

packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// found in the LICENSE file.
44

55
#import "FLTImagePickerPlugin.h"
6+
#import "FLTImagePickerPlugin_Test.h"
67

78
#import <AVFoundation/AVFoundation.h>
89
#import <MobileCoreServices/MobileCoreServices.h>
@@ -21,8 +22,6 @@ @interface FLTImagePickerPlugin () <UINavigationControllerDelegate,
2122
PHPickerViewControllerDelegate,
2223
UIAdaptivePresentationControllerDelegate>
2324

24-
@property(copy, nonatomic) FlutterResult result;
25-
2625
@property(assign, nonatomic) int maxImagesAllowed;
2726

2827
@property(copy, nonatomic) NSDictionary *arguments;
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
// This header is available in the Test module. Import via "@import image_picker.Test;"
6+
7+
#import <image_picker/FLTImagePickerPlugin.h>
8+
9+
/** Methods exposed for unit testing. */
10+
@interface FLTImagePickerPlugin ()
11+
12+
/** The Flutter result callback use to report results back to Flutter App. */
13+
@property(copy, nonatomic) FlutterResult result;
14+
15+
/**
16+
* Applies NSMutableArray on the FLutterResult.
17+
*
18+
* NSString must be returned by FlutterResult if the single image
19+
* mode is active. It is checked by maxImagesAllowed and
20+
* returns the first object of the pathlist.
21+
*
22+
* NSMutableArray must be returned by FlutterResult if the multi-image
23+
* mode is active. After the pathlist count is checked then it returns
24+
* the pathlist.
25+
*
26+
* @param pathList that should be applied to FlutterResult.
27+
*/
28+
- (void)handleSavedPathList:(NSArray *)pathList;
29+
30+
/**
31+
* Tells the delegate that the user cancelled the pick operation.
32+
*
33+
* Your delegate’s implementation of this method should dismiss the picker view
34+
* by calling the dismissModalViewControllerAnimated: method of the parent
35+
* view controller.
36+
*
37+
* Implementation of this method is optional, but expected.
38+
*
39+
* @param picker The controller object managing the image picker interface.
40+
*/
41+
-(void)imagePickerControllerDidCancel : (UIImagePickerController *)picker;
42+
43+
@end
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
framework module image_picker {
2+
umbrella header "image_picker-umbrella.h"
3+
4+
export *
5+
module * { export * }
6+
7+
explicit module Test {
8+
header "FLTImagePickerPlugin_Test.h"
9+
}
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#import <Foundation/Foundation.h>
6+
#import <image_picker/FLTImagePickerImageUtil.h>
7+
#import <image_picker/FLTImagePickerMetaDataUtil.h>
8+
#import <image_picker/FLTImagePickerPhotoAssetUtil.h>
9+
#import <image_picker/FLTImagePickerPlugin.h>
10+
#import <image_picker/FLTPHPickerSaveImageToPathOperation.h>

packages/image_picker/image_picker/ios/image_picker.podspec

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ Downloaded by pub (not CocoaPods).
1414
s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' }
1515
s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/image_picker' }
1616
s.documentation_url = 'https://pub.dev/packages/image_picker'
17-
s.source_files = 'Classes/**/*'
17+
s.source_files = 'Classes/**/*.{h,m}'
1818
s.public_header_files = 'Classes/**/*.h'
19+
s.module_map = 'Classes/ImagePickerPlugin.modulemap'
1920
s.dependency 'Flutter'
2021
s.platform = :ios, '9.0'
2122
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }

packages/image_picker/image_picker/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ description: Flutter plugin for selecting images from the Android and iOS image
33
library, and taking new pictures with the camera.
44
repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker
55
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22
6-
version: 0.8.4+6
6+
version: 0.8.4+7
77

88
environment:
99
sdk: ">=2.14.0 <3.0.0"

packages/video_player/video_player/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 2.2.17
2+
3+
* Avoid blocking the main thread loading video count on iOS.
4+
15
## 2.2.16
26

37
* Introduces `setCaptionOffset` to offset the caption display based on a Duration.

packages/video_player/video_player/example/ios/RunnerTests/VideoPlayerTests.m

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
#import <OCMock/OCMock.h>
1010

11-
@interface FLTVideoPlayer : NSObject
11+
@interface FLTVideoPlayer : NSObject <FlutterStreamHandler>
1212
@property(readonly, nonatomic) AVPlayer *player;
1313
@end
1414

@@ -70,4 +70,91 @@ - (void)testDeregistersFromPlayer {
7070
[self waitForExpectationsWithTimeout:1 handler:nil];
7171
}
7272

73+
- (void)testVideoControls {
74+
NSObject<FlutterPluginRegistry> *registry =
75+
(NSObject<FlutterPluginRegistry> *)[[UIApplication sharedApplication] delegate];
76+
NSObject<FlutterPluginRegistrar> *registrar = [registry registrarForPlugin:@"TestVideoControls"];
77+
78+
FLTVideoPlayerPlugin *videoPlayerPlugin =
79+
(FLTVideoPlayerPlugin *)[[FLTVideoPlayerPlugin alloc] initWithRegistrar:registrar];
80+
81+
NSDictionary<NSString *, id> *videoInitialization =
82+
[self testPlugin:videoPlayerPlugin
83+
uri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4"];
84+
XCTAssertEqualObjects(videoInitialization[@"height"], @720);
85+
XCTAssertEqualObjects(videoInitialization[@"width"], @1280);
86+
XCTAssertEqualWithAccuracy([videoInitialization[@"duration"] intValue], 4000, 200);
87+
}
88+
89+
- (void)testAudioControls {
90+
NSObject<FlutterPluginRegistry> *registry =
91+
(NSObject<FlutterPluginRegistry> *)[[UIApplication sharedApplication] delegate];
92+
NSObject<FlutterPluginRegistrar> *registrar = [registry registrarForPlugin:@"TestAudioControls"];
93+
94+
FLTVideoPlayerPlugin *videoPlayerPlugin =
95+
(FLTVideoPlayerPlugin *)[[FLTVideoPlayerPlugin alloc] initWithRegistrar:registrar];
96+
97+
NSDictionary<NSString *, id> *audioInitialization =
98+
[self testPlugin:videoPlayerPlugin
99+
uri:@"https://cdn.pixabay.com/audio/2021/09/06/audio_bacd4d6020.mp3"];
100+
XCTAssertEqualObjects(audioInitialization[@"height"], @0);
101+
XCTAssertEqualObjects(audioInitialization[@"width"], @0);
102+
// Perfect precision not guaranteed.
103+
XCTAssertEqualWithAccuracy([audioInitialization[@"duration"] intValue], 68500, 200);
104+
}
105+
106+
- (NSDictionary<NSString *, id> *)testPlugin:(FLTVideoPlayerPlugin *)videoPlayerPlugin
107+
uri:(NSString *)uri {
108+
FlutterError *error;
109+
[videoPlayerPlugin initialize:&error];
110+
XCTAssertNil(error);
111+
112+
FLTCreateMessage *create = [[FLTCreateMessage alloc] init];
113+
create.uri = uri;
114+
FLTTextureMessage *textureMessage = [videoPlayerPlugin create:create error:&error];
115+
116+
NSNumber *textureId = textureMessage.textureId;
117+
FLTVideoPlayer *player = videoPlayerPlugin.playersByTextureId[textureId];
118+
XCTAssertNotNil(player);
119+
120+
XCTestExpectation *initializedExpectation = [self expectationWithDescription:@"initialized"];
121+
__block NSDictionary<NSString *, id> *initializationEvent;
122+
[player onListenWithArguments:nil
123+
eventSink:^(NSDictionary<NSString *, id> *event) {
124+
if ([event[@"event"] isEqualToString:@"initialized"]) {
125+
initializationEvent = event;
126+
XCTAssertEqual(event.count, 4);
127+
[initializedExpectation fulfill];
128+
}
129+
}];
130+
[self waitForExpectationsWithTimeout:1.0 handler:nil];
131+
132+
// Starts paused.
133+
AVPlayer *avPlayer = player.player;
134+
XCTAssertEqual(avPlayer.rate, 0);
135+
XCTAssertEqual(avPlayer.volume, 1);
136+
XCTAssertEqual(avPlayer.timeControlStatus, AVPlayerTimeControlStatusPaused);
137+
138+
// Change playback speed.
139+
FLTPlaybackSpeedMessage *playback = [[FLTPlaybackSpeedMessage alloc] init];
140+
playback.textureId = textureId;
141+
playback.speed = @2;
142+
[videoPlayerPlugin setPlaybackSpeed:playback error:&error];
143+
XCTAssertNil(error);
144+
XCTAssertEqual(avPlayer.rate, 2);
145+
XCTAssertEqual(avPlayer.timeControlStatus, AVPlayerTimeControlStatusWaitingToPlayAtSpecifiedRate);
146+
147+
// Volume
148+
FLTVolumeMessage *volume = [[FLTVolumeMessage alloc] init];
149+
volume.textureId = textureId;
150+
volume.volume = @(0.1);
151+
[videoPlayerPlugin setVolume:volume error:&error];
152+
XCTAssertNil(error);
153+
XCTAssertEqual(avPlayer.volume, 0.1f);
154+
155+
[player onCancelWithArguments:nil];
156+
157+
return initializationEvent;
158+
}
159+
73160
@end

packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -331,25 +331,44 @@ - (void)updatePlayingState {
331331

332332
- (void)setupEventSinkIfReadyToPlay {
333333
if (_eventSink && !_isInitialized) {
334-
BOOL hasVideoTracks =
335-
[[self.player.currentItem.asset tracksWithMediaType:AVMediaTypeVideo] count] != 0;
336-
CGSize size = [self.player currentItem].presentationSize;
334+
AVPlayerItem *currentItem = self.player.currentItem;
335+
CGSize size = currentItem.presentationSize;
337336
CGFloat width = size.width;
338337
CGFloat height = size.height;
339338

339+
// Wait until tracks are loaded to check duration or if there are any videos.
340+
AVAsset *asset = currentItem.asset;
341+
if ([asset statusOfValueForKey:@"tracks" error:nil] != AVKeyValueStatusLoaded) {
342+
void (^trackCompletionHandler)(void) = ^{
343+
if ([asset statusOfValueForKey:@"tracks" error:nil] != AVKeyValueStatusLoaded) {
344+
// Cancelled, or something failed.
345+
return;
346+
}
347+
// This completion block will run on an AVFoundation background queue.
348+
// Hop back to the main thread to set up event sink.
349+
[self performSelector:_cmd onThread:NSThread.mainThread withObject:self waitUntilDone:NO];
350+
};
351+
[asset loadValuesAsynchronouslyForKeys:@[ @"tracks" ]
352+
completionHandler:trackCompletionHandler];
353+
return;
354+
}
355+
356+
BOOL hasVideoTracks = [asset tracksWithMediaType:AVMediaTypeVideo].count != 0;
357+
340358
// The player has not yet initialized when it contains video tracks.
341359
if (hasVideoTracks && height == CGSizeZero.height && width == CGSizeZero.width) {
342360
return;
343361
}
344362
// The player may be initialized but still needs to determine the duration.
345-
if ([self duration] == 0) {
363+
int64_t duration = [self duration];
364+
if (duration == 0) {
346365
return;
347366
}
348367

349368
_isInitialized = YES;
350369
_eventSink(@{
351370
@"event" : @"initialized",
352-
@"duration" : @([self duration]),
371+
@"duration" : @(duration),
353372
@"width" : @(width),
354373
@"height" : @(height)
355374
});

packages/video_player/video_player/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ description: Flutter plugin for displaying inline video with other Flutter
33
widgets on Android, iOS, and web.
44
repository: https://github.com/flutter/plugins/tree/main/packages/video_player/video_player
55
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22
6-
version: 2.2.16
6+
version: 2.2.17
77

88
environment:
99
sdk: ">=2.14.0 <3.0.0"

0 commit comments

Comments
 (0)