8
8
9
9
#import < OCMock/OCMock.h>
10
10
#import < video_player_avfoundation/AVAssetTrackUtils.h>
11
+ #import < video_player_avfoundation/FVPEventBridge.h>
11
12
#import < video_player_avfoundation/FVPNativeVideoViewFactory.h>
12
13
#import < video_player_avfoundation/FVPTextureBasedVideoPlayer_Test.h>
13
14
#import < video_player_avfoundation/FVPVideoPlayerPlugin_Test.h>
@@ -166,6 +167,55 @@ - (instancetype)init {
166
167
167
168
#pragma mark -
168
169
170
+ @interface StubEventListener : NSObject <FVPVideoEventListener>
171
+
172
+ @property (nonatomic ) XCTestExpectation *initializationExpectation;
173
+ @property (nonatomic ) int64_t initializationDuration;
174
+ @property (nonatomic ) CGSize initializationSize;
175
+
176
+ - (instancetype )initWithInitializationExpectation : (XCTestExpectation *)expectation ;
177
+
178
+ @end
179
+
180
+ @implementation StubEventListener
181
+
182
+ - (instancetype )initWithInitializationExpectation : (XCTestExpectation *)expectation {
183
+ self = [super init ];
184
+ _initializationExpectation = expectation;
185
+ return self;
186
+ }
187
+
188
+ - (void )videoPlayerDidComplete {
189
+ }
190
+
191
+ - (void )videoPlayerDidEndBuffering {
192
+ }
193
+
194
+ - (void )videoPlayerDidErrorWithMessage : (NSString *)errorMessage {
195
+ }
196
+
197
+ - (void )videoPlayerDidInitializeWithDuration : (int64_t )duration size : (CGSize)size {
198
+ [self .initializationExpectation fulfill ];
199
+ self.initializationDuration = duration;
200
+ self.initializationSize = size;
201
+ }
202
+
203
+ - (void )videoPlayerDidSetPlaying : (BOOL )playing {
204
+ }
205
+
206
+ - (void )videoPlayerDidStartBuffering {
207
+ }
208
+
209
+ - (void )videoPlayerDidUpdateBufferRegions : (NSArray <NSArray<NSNumber *> *> *)regions {
210
+ }
211
+
212
+ - (void )videoPlayerWasDisposed {
213
+ }
214
+
215
+ @end
216
+
217
+ #pragma mark -
218
+
169
219
@implementation VideoPlayerTests
170
220
171
221
- (void )testBlankVideoBugWithEncryptedVideoStreamAndInvertedAspectRatioBugForSomeVideoStream {
@@ -220,9 +270,9 @@ - (void)testPlayerForPlatformViewDoesNotRegisterTexture {
220
270
viewProvider: [[StubViewProvider alloc ] initWithView: nil ]
221
271
registrar: registrar];
222
272
223
- FlutterError *initalizationError ;
224
- [videoPlayerPlugin initialize: &initalizationError ];
225
- XCTAssertNil (initalizationError );
273
+ FlutterError *initializationError ;
274
+ [videoPlayerPlugin initialize: &initializationError ];
275
+ XCTAssertNil (initializationError );
226
276
FVPCreationOptions *create = [FVPCreationOptions
227
277
makeWithUri: @" https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8"
228
278
httpHeaders: @{}
@@ -248,9 +298,9 @@ - (void)testSeekToWhilePausedStartsDisplayLinkTemporarily {
248
298
viewProvider: [[StubViewProvider alloc ] initWithView: nil ]
249
299
registrar: registrar];
250
300
251
- FlutterError *initalizationError ;
252
- [videoPlayerPlugin initialize: &initalizationError ];
253
- XCTAssertNil (initalizationError );
301
+ FlutterError *initializationError ;
302
+ [videoPlayerPlugin initialize: &initializationError ];
303
+ XCTAssertNil (initializationError );
254
304
FVPCreationOptions *create = [FVPCreationOptions
255
305
makeWithUri: @" https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8"
256
306
httpHeaders: @{}
@@ -305,9 +355,9 @@ - (void)testInitStartsDisplayLinkTemporarily {
305
355
viewProvider: [[StubViewProvider alloc ] initWithView: nil ]
306
356
registrar: registrar];
307
357
308
- FlutterError *initalizationError ;
309
- [videoPlayerPlugin initialize: &initalizationError ];
310
- XCTAssertNil (initalizationError );
358
+ FlutterError *initializationError ;
359
+ [videoPlayerPlugin initialize: &initializationError ];
360
+ XCTAssertNil (initializationError );
311
361
FVPCreationOptions *create = [FVPCreationOptions
312
362
makeWithUri: @" https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8"
313
363
httpHeaders: @{}
@@ -351,9 +401,9 @@ - (void)testSeekToWhilePlayingDoesNotStopDisplayLink {
351
401
viewProvider: [[StubViewProvider alloc ] initWithView: nil ]
352
402
registrar: registrar];
353
403
354
- FlutterError *initalizationError ;
355
- [videoPlayerPlugin initialize: &initalizationError ];
356
- XCTAssertNil (initalizationError );
404
+ FlutterError *initializationError ;
405
+ [videoPlayerPlugin initialize: &initializationError ];
406
+ XCTAssertNil (initializationError );
357
407
FVPCreationOptions *create = [FVPCreationOptions
358
408
makeWithUri: @" https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8"
359
409
httpHeaders: @{}
@@ -406,9 +456,9 @@ - (void)testPauseWhileWaitingForFrameDoesNotStopDisplayLink {
406
456
viewProvider: [[StubViewProvider alloc ] initWithView: nil ]
407
457
registrar: registrar];
408
458
409
- FlutterError *initalizationError ;
410
- [videoPlayerPlugin initialize: &initalizationError ];
411
- XCTAssertNil (initalizationError );
459
+ FlutterError *initializationError ;
460
+ [videoPlayerPlugin initialize: &initializationError ];
461
+ XCTAssertNil (initializationError );
412
462
FVPCreationOptions *create = [FVPCreationOptions
413
463
makeWithUri: @" https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8"
414
464
httpHeaders: @{}
@@ -477,16 +527,19 @@ - (void)testBufferingStateFromPlayer {
477
527
AVPlayer *avPlayer = player.player ;
478
528
[avPlayer play ];
479
529
480
- [player onListenWithArguments: nil
481
- eventSink: ^(NSDictionary <NSString *, id > *event) {
482
- if ([event[@" event" ] isEqualToString: @" bufferingEnd" ]) {
483
- XCTAssertTrue (avPlayer.currentItem .isPlaybackLikelyToKeepUp );
484
- }
485
-
486
- if ([event[@" event" ] isEqualToString: @" bufferingStart" ]) {
487
- XCTAssertFalse (avPlayer.currentItem .isPlaybackLikelyToKeepUp );
488
- }
489
- }];
530
+ // TODO(stuartmorgan): Update this test to instead use a mock listener, and add separate unit
531
+ // tests of FVPEventBridge.
532
+ [(NSObject <FlutterStreamHandler> *)player.eventListener
533
+ onListenWithArguments: nil
534
+ eventSink: ^(NSDictionary <NSString *, id > *event) {
535
+ if ([event[@" event" ] isEqualToString: @" bufferingEnd" ]) {
536
+ XCTAssertTrue (avPlayer.currentItem .isPlaybackLikelyToKeepUp );
537
+ }
538
+
539
+ if ([event[@" event" ] isEqualToString: @" bufferingStart" ]) {
540
+ XCTAssertFalse (avPlayer.currentItem .isPlaybackLikelyToKeepUp );
541
+ }
542
+ }];
490
543
XCTestExpectation *bufferingStateExpectation =
491
544
[self expectationWithDescription: @" bufferingState" ];
492
545
NSTimeInterval timeout = 10 ;
@@ -498,39 +551,39 @@ - (void)testBufferingStateFromPlayer {
498
551
}
499
552
500
553
- (void )testVideoControls {
501
- NSDictionary < NSString *, id > *videoInitialization =
554
+ StubEventListener *eventListener =
502
555
[self sanityTestURI: @" https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4" ];
503
- XCTAssertEqualObjects (videoInitialization[ @" height" ], @ 720 );
504
- XCTAssertEqualObjects (videoInitialization[ @" width" ], @ 1280 );
505
- XCTAssertEqualWithAccuracy ([videoInitialization[ @" duration " ] intValue ] , 4000 , 200 );
556
+ XCTAssertEqual (eventListener. initializationSize . height , 720 );
557
+ XCTAssertEqual (eventListener. initializationSize . width , 1280 );
558
+ XCTAssertEqualWithAccuracy (eventListener. initializationDuration , 4000 , 200 );
506
559
}
507
560
508
561
- (void )testAudioControls {
509
- NSDictionary < NSString *, id > *audioInitialization = [self
562
+ StubEventListener *eventListener = [self
510
563
sanityTestURI: @" https://flutter.github.io/assets-for-api-docs/assets/audio/rooster.mp3" ];
511
- XCTAssertEqualObjects (audioInitialization[ @" height" ], @ 0 );
512
- XCTAssertEqualObjects (audioInitialization[ @" width" ], @ 0 );
564
+ XCTAssertEqual (eventListener. initializationSize . height , 0 );
565
+ XCTAssertEqual (eventListener. initializationSize . width , 0 );
513
566
// Perfect precision not guaranteed.
514
- XCTAssertEqualWithAccuracy ([audioInitialization[ @" duration " ] intValue ] , 5400 , 200 );
567
+ XCTAssertEqualWithAccuracy (eventListener. initializationDuration , 5400 , 200 );
515
568
}
516
569
517
570
- (void )testHLSControls {
518
- NSDictionary < NSString *, id > *videoInitialization = [self
571
+ StubEventListener *eventListener = [self
519
572
sanityTestURI: @" https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8" ];
520
- XCTAssertEqualObjects (videoInitialization[ @" height" ], @ 720 );
521
- XCTAssertEqualObjects (videoInitialization[ @" width" ], @ 1280 );
522
- XCTAssertEqualWithAccuracy ([videoInitialization[ @" duration " ] intValue ] , 4000 , 200 );
573
+ XCTAssertEqual (eventListener. initializationSize . height , 720 );
574
+ XCTAssertEqual (eventListener. initializationSize . width , 1280 );
575
+ XCTAssertEqualWithAccuracy (eventListener. initializationDuration , 4000 , 200 );
523
576
}
524
577
525
578
- (void )testAudioOnlyHLSControls {
526
579
XCTSkip (@" Flaky; see https://github.com/flutter/flutter/issues/164381" );
527
580
528
- NSDictionary < NSString *, id > *videoInitialization =
581
+ StubEventListener *eventListener =
529
582
[self sanityTestURI: @" https://flutter.github.io/assets-for-api-docs/assets/videos/hls/"
530
583
@" bee_audio_only.m3u8" ];
531
- XCTAssertEqualObjects (videoInitialization[ @" height" ], @ 0 );
532
- XCTAssertEqualObjects (videoInitialization[ @" width" ], @ 0 );
533
- XCTAssertEqualWithAccuracy ([videoInitialization[ @" duration " ] intValue ] , 4000 , 200 );
584
+ XCTAssertEqual (eventListener. initializationSize . height , 0 );
585
+ XCTAssertEqual (eventListener. initializationSize . width , 0 );
586
+ XCTAssertEqualWithAccuracy (eventListener. initializationDuration , 4000 , 200 );
534
587
}
535
588
536
589
#if TARGET_OS_IOS
@@ -555,6 +608,8 @@ - (void)testSeekToleranceWhenNotSeekingToEnd {
555
608
httpHeaders: @{}
556
609
avFactory: stubAVFactory
557
610
viewProvider: [[StubViewProvider alloc ] initWithView: nil ]];
611
+ NSObject <FVPVideoEventListener> *listener = OCMProtocolMock (@protocol (FVPVideoEventListener));
612
+ player.eventListener = listener;
558
613
559
614
XCTestExpectation *seekExpectation =
560
615
[self expectationWithDescription: @" seekTo has zero tolerance when seeking not to end" ];
@@ -577,6 +632,8 @@ - (void)testSeekToleranceWhenSeekingToEnd {
577
632
httpHeaders: @{}
578
633
avFactory: stubAVFactory
579
634
viewProvider: [[StubViewProvider alloc ] initWithView: nil ]];
635
+ NSObject <FVPVideoEventListener> *listener = OCMProtocolMock (@protocol (FVPVideoEventListener));
636
+ player.eventListener = listener;
580
637
581
638
XCTestExpectation *seekExpectation =
582
639
[self expectationWithDescription: @" seekTo has non-zero tolerance when seeking to end" ];
@@ -592,7 +649,9 @@ - (void)testSeekToleranceWhenSeekingToEnd {
592
649
593
650
// / Sanity checks a video player playing the given URL with the actual AVPlayer. This is essentially
594
651
// / a mini integration test of the player component.
595
- - (NSDictionary <NSString *, id> *)sanityTestURI : (NSString *)testURI {
652
+ // /
653
+ // / Returns the stub event listener to allow tests to inspect the call state.
654
+ - (StubEventListener *)sanityTestURI : (NSString *)testURI {
596
655
NSURL *testURL = [NSURL URLWithString: testURI];
597
656
XCTAssertNotNil (testURL);
598
657
FVPVideoPlayer *player =
@@ -603,15 +662,9 @@ - (void)testSeekToleranceWhenSeekingToEnd {
603
662
XCTAssertNotNil (player);
604
663
605
664
XCTestExpectation *initializedExpectation = [self expectationWithDescription: @" initialized" ];
606
- __block NSDictionary <NSString *, id > *initializationEvent;
607
- [player onListenWithArguments: nil
608
- eventSink: ^(NSDictionary <NSString *, id > *event) {
609
- if ([event[@" event" ] isEqualToString: @" initialized" ]) {
610
- initializationEvent = event;
611
- XCTAssertEqual (event.count , 4 );
612
- [initializedExpectation fulfill ];
613
- }
614
- }];
665
+ StubEventListener *listener =
666
+ [[StubEventListener alloc ] initWithInitializationExpectation: initializedExpectation];
667
+ player.eventListener = listener;
615
668
[self waitForExpectationsWithTimeout: 30.0 handler: nil ];
616
669
617
670
// Starts paused.
@@ -634,9 +687,7 @@ - (void)testSeekToleranceWhenSeekingToEnd {
634
687
XCTAssertNil (error);
635
688
XCTAssertEqual (avPlayer.volume , 0 .1f );
636
689
637
- [player onCancelWithArguments: nil ];
638
-
639
- return initializationEvent;
690
+ return listener;
640
691
}
641
692
642
693
// Checks whether [AVPlayer rate] KVO observations are correctly detached.
@@ -787,12 +838,15 @@ - (void)testFailedToLoadVideoEventShouldBeAlwaysSent {
787
838
[self waitForExpectationsWithTimeout: 10.0 handler: nil ];
788
839
789
840
XCTestExpectation *failedExpectation = [self expectationWithDescription: @" failed" ];
790
- [player onListenWithArguments: nil
791
- eventSink: ^(FlutterError *event) {
792
- if ([event isKindOfClass: FlutterError.class]) {
793
- [failedExpectation fulfill ];
794
- }
795
- }];
841
+ // TODO(stuartmorgan): Update this test to instead use a mock listener, and add separate unit
842
+ // tests of FVPEventBridge.
843
+ [(NSObject <FlutterStreamHandler> *)player.eventListener
844
+ onListenWithArguments: nil
845
+ eventSink: ^(FlutterError *event) {
846
+ if ([event isKindOfClass: FlutterError.class]) {
847
+ [failedExpectation fulfill ];
848
+ }
849
+ }];
796
850
[self waitForExpectationsWithTimeout: 10.0 handler: nil ];
797
851
}
798
852
@@ -804,12 +858,9 @@ - (void)testUpdatePlayingStateShouldNotResetRate {
804
858
viewProvider: [[StubViewProvider alloc ] initWithView: nil ]];
805
859
806
860
XCTestExpectation *initializedExpectation = [self expectationWithDescription: @" initialized" ];
807
- [player onListenWithArguments: nil
808
- eventSink: ^(NSDictionary <NSString *, id > *event) {
809
- if ([event[@" event" ] isEqualToString: @" initialized" ]) {
810
- [initializedExpectation fulfill ];
811
- }
812
- }];
861
+ StubEventListener *listener =
862
+ [[StubEventListener alloc ] initWithInitializationExpectation: initializedExpectation];
863
+ player.eventListener = listener;
813
864
[self waitForExpectationsWithTimeout: 10 handler: nil ];
814
865
815
866
FlutterError *error;
0 commit comments