diff --git a/.rubocop.yml b/.rubocop.yml index ec08ab70..038455e6 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -4,16 +4,15 @@ inherit_mode: AllCops: Exclude: - - 'Carthage/**/*' - - 'Demo/Carthage/**/*' - 'vendor/**/*' Include: - 'fastlane/Pluginfile' + NewCops: enable # this would cause errors with long lanes Metrics/BlockLength: Enabled: true - ExcludedMethods: ['platform', 'for_platform'] + IgnoredMethods: ['platform', 'for_platform'] # Lane description and gem lines can be long Layout/LineLength: diff --git a/.swiftlint.yml b/.swiftlint.yml new file mode 100644 index 00000000..78548db3 --- /dev/null +++ b/.swiftlint.yml @@ -0,0 +1,17 @@ +disabled_rules: + - identifier_name + - statement_position + - todo + +line_length: 300 + +trailing_whitespace: + ignores_empty_lines: true + severity: warning + +nesting: + type_level: 2 + +excluded: + - .build + - vendor diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/SRGMediaPlayer.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/SRGMediaPlayer.xcscheme index 01dd72eb..0d296c7a 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/SRGMediaPlayer.xcscheme +++ b/.swiftpm/xcode/xcshareddata/xcschemes/SRGMediaPlayer.xcscheme @@ -1,6 +1,6 @@ + + + + + + + + diff --git a/Demo/Demo.xcconfig b/Demo/Demo.xcconfig index 311b1535..de873fd0 100644 --- a/Demo/Demo.xcconfig +++ b/Demo/Demo.xcconfig @@ -1,5 +1,5 @@ // Version information -MARKETING_VERSION = 6.2.1 +MARKETING_VERSION = 6.2.2 // Deployment targets IPHONEOS_DEPLOYMENT_TARGET = 9.0 diff --git a/Demo/Resources/Data/AudioDemoConfiguration.plist b/Demo/Resources/Data/AudioDemoConfiguration.plist index 9cef1699..c308ac73 100644 --- a/Demo/Resources/Data/AudioDemoConfiguration.plist +++ b/Demo/Resources/Data/AudioDemoConfiguration.plist @@ -20,13 +20,7 @@ name C'est pas trop tôt - AOD MP3 url - https://rtsww-a-d.rts.ch/la-1ere/programmes/c-est-pas-trop-tot/2017/c-est-pas-trop-tot_20170628_full_c-est-pas-trop-tot_007d77e7-61fb-4aef-9491-5e6b07f7f931-128k.mp3 - - - name - Forum - AOD MP3 - url - https://rtsww-a-d.rts.ch/la-1ere/programmes/forum/2020/forum_20200106_full_forum_838f8c3d-a3b8-42d1-a6a2-68b12291b157-128k.mp3 + https://rts-aod-dd.akamaized.net/ww/8849864/73bab428-ce6e-3ded-92cf-c84649ed766f.mp3 name diff --git a/Demo/Resources/Data/MultiPlayerDemoConfiguration.plist b/Demo/Resources/Data/MultiPlayerDemoConfiguration.plist index d8adefec..98ec4fae 100644 --- a/Demo/Resources/Data/MultiPlayerDemoConfiguration.plist +++ b/Demo/Resources/Data/MultiPlayerDemoConfiguration.plist @@ -12,9 +12,9 @@ name - Tagesschau + Livestream url - http://tagesschau-lh.akamaihd.net/i/tagesschau_1@119231/master.m3u8?dw=0 + https://rtsc3video-lh.akamaihd.net/i/rtsc3video_ww@513975/master.m3u8?dw=0 name diff --git a/Demo/Resources/Data/SegmentDemoConfiguration.plist b/Demo/Resources/Data/SegmentDemoConfiguration.plist index 80e17363..0aed1a14 100644 --- a/Demo/Resources/Data/SegmentDemoConfiguration.plist +++ b/Demo/Resources/Data/SegmentDemoConfiguration.plist @@ -325,7 +325,7 @@ name Segments in DVR stream url - http://tagesschau-lh.akamaihd.net/i/tagesschau_1@119231/master.m3u8 + https://rtsc3video-lh.akamaihd.net/i/rtsc3video_ww@513975/master.m3u8 segments diff --git a/Demo/Resources/Data/VideoDemoConfiguration.plist b/Demo/Resources/Data/VideoDemoConfiguration.plist index 527e2ea9..19ae7fc3 100644 --- a/Demo/Resources/Data/VideoDemoConfiguration.plist +++ b/Demo/Resources/Data/VideoDemoConfiguration.plist @@ -8,19 +8,19 @@ name Livestream url - http://tagesschau-lh.akamaihd.net/i/tagesschau_1@119231/master.m3u8?dw=0 + https://rtsc3video-lh.akamaihd.net/i/rtsc3video_ww@513975/master.m3u8?dw=0 name Livestream with DVR url - http://tagesschau-lh.akamaihd.net/i/tagesschau_1@119231/master.m3u8 + https://rtsc3video-lh.akamaihd.net/i/rtsc3video_ww@513975/master.m3u8 name Livestream with DVR (with timestamps) url - https://mcdn.daserste.de/daserste/int/master.m3u8 + https://tagesschau.akamaized.net/hls/live/2020115/tagesschau/tagesschau_1/master.m3u8 name @@ -56,7 +56,7 @@ name Bonjour la Suisse - 4 audios (+ AD) & FR SDH subtitles url - https://rts-vod-amd.akamaized.net/ww/hls/8806923/ead53ddc-4703-35bd-b6a2-c0a9231a4d62/master.m3u8 + https://rts-vod-amd.akamaized.net/ww/8806923/f896dc42-b777-387e-9767-9e8821b502e9/master.m3u8 name diff --git a/Demo/SRGMediaPlayer-demo.xcodeproj/project.pbxproj b/Demo/SRGMediaPlayer-demo.xcodeproj/project.pbxproj index 91ea5a5b..eeb8b6da 100644 --- a/Demo/SRGMediaPlayer-demo.xcodeproj/project.pbxproj +++ b/Demo/SRGMediaPlayer-demo.xcodeproj/project.pbxproj @@ -358,7 +358,7 @@ isa = PBXProject; attributes = { CLASSPREFIX = SRG; - LastUpgradeCheck = 1310; + LastUpgradeCheck = 1330; ORGANIZATIONNAME = "SRG SSR"; TargetAttributes = { E69A1E661D61BEFC0064E6C1 = { diff --git a/Demo/SRGMediaPlayer-demo.xcodeproj/xcshareddata/xcschemes/SRGMediaPlayer-demo.xcscheme b/Demo/SRGMediaPlayer-demo.xcodeproj/xcshareddata/xcschemes/SRGMediaPlayer-demo.xcscheme index 2b4384b6..b06e1ac1 100644 --- a/Demo/SRGMediaPlayer-demo.xcodeproj/xcshareddata/xcschemes/SRGMediaPlayer-demo.xcscheme +++ b/Demo/SRGMediaPlayer-demo.xcodeproj/xcshareddata/xcschemes/SRGMediaPlayer-demo.xcscheme @@ -1,6 +1,6 @@ = 2.0.2, < 5.0) artifactory (3.0.15) atomos (0.1.3) aws-eventstream (1.2.0) - aws-partitions (1.502.0) - aws-sdk-core (3.121.0) + aws-partitions (1.554.0) + aws-sdk-core (3.126.0) aws-eventstream (~> 1, >= 1.0.2) - aws-partitions (~> 1, >= 1.239.0) + aws-partitions (~> 1, >= 1.525.0) aws-sigv4 (~> 1.1) jmespath (~> 1.0) - aws-sdk-kms (1.48.0) - aws-sdk-core (~> 3, >= 3.120.0) + aws-sdk-kms (1.54.0) + aws-sdk-core (~> 3, >= 3.126.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.103.0) - aws-sdk-core (~> 3, >= 3.120.0) + aws-sdk-s3 (1.112.0) + aws-sdk-core (~> 3, >= 3.126.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.4) aws-sigv4 (1.4.0) @@ -34,18 +35,19 @@ GEM domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) dotenv (2.7.6) - emoji_regex (3.2.2) - excon (0.85.0) - faraday (1.7.2) + emoji_regex (3.2.3) + excon (0.91.0) + faraday (1.9.3) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) faraday-excon (~> 1.1) - faraday-httpclient (~> 1.0.1) + faraday-httpclient (~> 1.0) + faraday-multipart (~> 1.0) faraday-net_http (~> 1.0) - faraday-net_http_persistent (~> 1.1) + faraday-net_http_persistent (~> 1.0) faraday-patron (~> 1.0) faraday-rack (~> 1.0) - multipart-post (>= 1.2, < 3) + faraday-retry (~> 1.0) ruby2_keywords (>= 0.0.4) faraday-cookie_jar (0.0.7) faraday (>= 0.8.0) @@ -54,14 +56,17 @@ GEM faraday-em_synchrony (1.0.0) faraday-excon (1.1.0) faraday-httpclient (1.0.1) + faraday-multipart (1.0.3) + multipart-post (>= 1.2, < 3) faraday-net_http (1.0.1) faraday-net_http_persistent (1.2.0) faraday-patron (1.0.0) faraday-rack (1.0.0) - faraday_middleware (1.1.0) + faraday-retry (1.0.3) + faraday_middleware (1.2.0) faraday (~> 1.0) - fastimage (2.2.5) - fastlane (2.194.0) + fastimage (2.2.6) + fastlane (2.204.3) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) @@ -100,12 +105,10 @@ GEM xcodeproj (>= 1.13.0, < 2.0.0) xcpretty (~> 0.3.0) xcpretty-travis-formatter (>= 0.0.3) - fastlane-plugin-trainer (0.4.1) - trainer (>= 0.7.0) gh_inspector (1.1.3) - google-apis-androidpublisher_v3 (0.11.0) + google-apis-androidpublisher_v3 (0.16.0) google-apis-core (>= 0.4, < 2.a) - google-apis-core (0.4.1) + google-apis-core (0.4.2) addressable (~> 2.5, >= 2.5.1) googleauth (>= 0.16.2, < 2.a) httpclient (>= 2.8.1, < 3.a) @@ -114,49 +117,49 @@ GEM retriable (>= 2.0, < 4.a) rexml webrick - google-apis-iamcredentials_v1 (0.7.0) + google-apis-iamcredentials_v1 (0.10.0) google-apis-core (>= 0.4, < 2.a) - google-apis-playcustomapp_v1 (0.5.0) + google-apis-playcustomapp_v1 (0.7.0) google-apis-core (>= 0.4, < 2.a) - google-apis-storage_v1 (0.6.0) + google-apis-storage_v1 (0.11.0) google-apis-core (>= 0.4, < 2.a) google-cloud-core (1.6.0) google-cloud-env (~> 1.0) google-cloud-errors (~> 1.0) google-cloud-env (1.5.0) faraday (>= 0.17.3, < 2.0) - google-cloud-errors (1.1.0) - google-cloud-storage (1.34.1) - addressable (~> 2.5) + google-cloud-errors (1.2.0) + google-cloud-storage (1.36.1) + addressable (~> 2.8) digest-crc (~> 0.4) google-apis-iamcredentials_v1 (~> 0.1) google-apis-storage_v1 (~> 0.1) google-cloud-core (~> 1.6) googleauth (>= 0.16.2, < 2.a) mini_mime (~> 1.0) - googleauth (0.17.1) + googleauth (1.1.1) faraday (>= 0.17.3, < 2.0) jwt (>= 1.4, < 3.0) memoist (~> 0.16) multi_json (~> 1.11) os (>= 0.9, < 2.0) - signet (~> 0.15) + signet (>= 0.16, < 2.a) highline (2.0.3) http-cookie (1.0.4) domain_name (~> 0.5) httpclient (2.8.3) - jmespath (1.4.0) - json (2.5.1) - jwt (2.2.3) + jmespath (1.6.0) + json (2.6.1) + jwt (2.3.0) memoist (0.16.2) mini_magick (4.11.0) - mini_mime (1.1.1) + mini_mime (1.1.2) multi_json (1.15.0) multipart-post (2.0.0) nanaimo (0.3.0) naturally (2.2.1) optparse (0.1.1) - os (1.1.1) + os (1.1.4) plist (3.6.0) public_suffix (4.0.6) rake (13.0.6) @@ -181,10 +184,7 @@ GEM terminal-notifier (2.0.0) terminal-table (1.8.0) unicode-display_width (~> 1.1, >= 1.1.1) - trailblazer-option (0.1.1) - trainer (0.9.1) - fastlane (>= 2.25.0) - plist (>= 3.1.0, < 4.0.0) + trailblazer-option (0.1.2) tty-cursor (0.7.1) tty-screen (0.8.1) tty-spinner (0.9.3) @@ -216,7 +216,6 @@ PLATFORMS DEPENDENCIES fastlane - fastlane-plugin-trainer xcode-install BUNDLED WITH diff --git a/Package.swift b/Package.swift index 1cc40672..e1a8996a 100644 --- a/Package.swift +++ b/Package.swift @@ -3,7 +3,7 @@ import PackageDescription struct ProjectSettings { - static let marketingVersion: String = "6.2.1" + static let marketingVersion: String = "6.2.2" } let package = Package( diff --git a/Sources/SRGMediaPlayer/SRGMediaPlayerController.m b/Sources/SRGMediaPlayer/SRGMediaPlayerController.m index 583aeee4..663483c2 100644 --- a/Sources/SRGMediaPlayer/SRGMediaPlayerController.m +++ b/Sources/SRGMediaPlayer/SRGMediaPlayerController.m @@ -246,7 +246,7 @@ - (void)setPlayer:(SRGPlayer *)player } } else if (playerItem.status == AVPlayerItemStatusFailed) { - [self stopWithUserInfo:nil]; + [self stopWithUserInfo:nil releasePlayer:YES]; NSError *error = SRGMediaPlayerControllerError(playerItem.error); [NSNotificationCenter.defaultCenter postNotificationName:SRGMediaPlayerPlaybackDidFailNotification @@ -1046,7 +1046,7 @@ - (void)pause - (void)stop { - [self stopWithUserInfo:nil]; + [self stopWithUserInfo:nil releasePlayer:YES]; } - (void)seekToPosition:(SRGPosition *)position withCompletionHandler:(void (^)(BOOL))completionHandler @@ -1055,6 +1055,11 @@ - (void)seekToPosition:(SRGPosition *)position withCompletionHandler:(void (^)(B } - (void)reset +{ + [self resetAndReleasePlayer:YES]; +} + +- (void)resetAndReleasePlayer:(BOOL)releasePlayer { // Save previous state information NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; @@ -1077,7 +1082,7 @@ - (void)reset self.initialTargetSegment = nil; self.initialPosition = nil; - [self stopWithUserInfo:userInfo.copy]; + [self stopWithUserInfo:userInfo.copy releasePlayer:releasePlayer]; } #pragma mark Playback (convenience methods) @@ -1221,7 +1226,7 @@ - (void)prepareToPlayURLAsset:(AVURLAsset *)URLAsset SRGMediaPlayerLogDebug(@"Controller", @"Playing %@", URL); - [self reset]; + [self resetAndReleasePlayer:NO]; self.contentURL = URL; self.URLAsset = URLAsset; @@ -1302,7 +1307,7 @@ - (void)seekToPosition:(SRGPosition *)position inTargetSegment:(id)t } } -- (void)stopWithUserInfo:(NSDictionary *)userInfo +- (void)stopWithUserInfo:(NSDictionary *)userInfo releasePlayer:(BOOL)releasePlayer { if ([self isPictureInPictureActive]) { [self stopPictureInPicture]; @@ -1323,7 +1328,10 @@ - (void)stopWithUserInfo:(NSDictionary *)userInfo if (_selected) { fullUserInfo[SRGMediaPlayerPreviousSelectedSegmentKey] = self.currentSegment; } - self.player = nil; + + if (releasePlayer) { + self.player = nil; + } } _selected = NO; @@ -1669,11 +1677,14 @@ - (void)skipBlockedSegment:(id)segment withCompletionHandler:(void ( - (void)bindToPlayerViewController:(AVPlayerViewController *)playerViewController { - if (self.playerViewController) { + if (self.playerViewController != playerViewController) { self.playerViewController.player = nil; } - playerViewController.player = self.player; + if (playerViewController.player != self.player) { + playerViewController.player = self.player; + } + self.playerViewController = playerViewController; // AVPlayerViewController works well (e.g. playback won't freeze in the simulator after a few seconds) only if @@ -1687,7 +1698,9 @@ - (void)unbindFromCurrentPlayerViewController return; } - self.playerViewController.player = nil; + if (self.playerViewController.player) { + self.playerViewController.player = nil; + } self.playerViewController = nil; // Rebind the player @@ -1697,7 +1710,9 @@ - (void)unbindFromCurrentPlayerViewController - (void)attachPlayer:(AVPlayer *)player toView:(SRGMediaPlayerView *)view { if (self.playerViewController) { - self.playerViewController.player = player; + if (self.playerViewController.player != player) { + self.playerViewController.player = player; + } } else { view.player = player; @@ -1992,7 +2007,7 @@ - (void)srg_mediaPlayerController_playerItemDidPlayToEndTime:(NSNotification *)n - (void)srg_mediaPlayerController_playerItemFailedToPlayToEndTime:(NSNotification *)notification { - [self stopWithUserInfo:nil]; + [self stopWithUserInfo:nil releasePlayer:YES]; NSError *error = SRGMediaPlayerControllerError(notification.userInfo[AVPlayerItemFailedToPlayToEndTimeErrorKey]); [NSNotificationCenter.defaultCenter postNotificationName:SRGMediaPlayerPlaybackDidFailNotification diff --git a/Tests/SRGMediaPlayerTests/PlaybackTestCase.m b/Tests/SRGMediaPlayerTests/PlaybackTestCase.m index fee62042..1d0a6d41 100644 --- a/Tests/SRGMediaPlayerTests/PlaybackTestCase.m +++ b/Tests/SRGMediaPlayerTests/PlaybackTestCase.m @@ -23,22 +23,22 @@ static NSURL *LiveTestURL(void) { - return [NSURL URLWithString:@"http://tagesschau-lh.akamaihd.net/i/tagesschau_1@119231/master.m3u8?dw=0"]; + return [NSURL URLWithString:@"https://rtsc3video-lh.akamaihd.net/i/rtsc3video_ww@513975/master.m3u8?dw=0"]; } -static NSURL *DVRTestURL(void) +static NSURL *DVRNoTimestampTestURL(void) { - return [NSURL URLWithString:@"http://tagesschau-lh.akamaihd.net/i/tagesschau_1@119231/master.m3u8"]; + return [NSURL URLWithString:@"https://rtsc3video-lh.akamaihd.net/i/rtsc3video_ww@513975/master.m3u8"]; } static NSURL *DVRTimestampTestURL(void) { - return [NSURL URLWithString:@"https://mcdn.daserste.de/daserste/int/master.m3u8"]; + return [NSURL URLWithString:@"https://tagesschau.akamaized.net/hls/live/2020115/tagesschau/tagesschau_1/master.m3u8"]; } static NSURL *AudioOverHTTPTestURL(void) { - return [NSURL URLWithString:@"https://rtsww-a-d.rts.ch/la-1ere/programmes/c-est-pas-trop-tot/2017/c-est-pas-trop-tot_20170628_full_c-est-pas-trop-tot_007d77e7-61fb-4aef-9491-5e6b07f7f931-128k.mp3"]; + return [NSURL URLWithString:@"https://rts-aod-dd.akamaized.net/ww/8849864/73bab428-ce6e-3ded-92cf-c84649ed766f.mp3"]; } @interface PlaybackTestCase : MediaPlayerBaseTestCase @@ -283,7 +283,7 @@ - (void)testDVRPlaybackDefaultStart return [notification.userInfo[SRGMediaPlayerPlaybackStateKey] integerValue] == SRGMediaPlayerPlaybackStatePlaying; }]; - [self.mediaPlayerController playURL:DVRTestURL() atPosition:nil withSegments:nil userInfo:nil]; + [self.mediaPlayerController playURL:DVRNoTimestampTestURL() atPosition:nil withSegments:nil userInfo:nil]; [self waitForExpectationsWithTimeout:30. handler:nil]; @@ -297,7 +297,7 @@ - (void)testDVRPlaybackStartAtTime return [notification.userInfo[SRGMediaPlayerPlaybackStateKey] integerValue] == SRGMediaPlayerPlaybackStatePlaying; }]; - [self.mediaPlayerController playURL:DVRTestURL() atPosition:[SRGPosition positionAtTimeInSeconds:20.] withSegments:nil userInfo:nil]; + [self.mediaPlayerController playURL:DVRNoTimestampTestURL() atPosition:[SRGPosition positionAtTimeInSeconds:20.] withSegments:nil userInfo:nil]; [self waitForExpectationsWithTimeout:30. handler:nil]; @@ -312,7 +312,7 @@ - (void)testDVRPlaybackStartAtDateWithoutTimestamps }]; NSDate *date = [NSDate dateWithTimeIntervalSinceNow:-10. * 60.]; - [self.mediaPlayerController playURL:DVRTestURL() atPosition:[SRGPosition positionAtDate:date] withSegments:nil userInfo:nil]; + [self.mediaPlayerController playURL:DVRNoTimestampTestURL() atPosition:[SRGPosition positionAtDate:date] withSegments:nil userInfo:nil]; [self waitForExpectationsWithTimeout:30. handler:nil]; @@ -1100,7 +1100,7 @@ - (void)testDVRProperties return [notification.userInfo[SRGMediaPlayerPlaybackStateKey] integerValue] == SRGMediaPlayerPlaybackStatePlaying; }]; - [self.mediaPlayerController playURL:DVRTestURL()]; + [self.mediaPlayerController playURL:DVRNoTimestampTestURL()]; [self waitForExpectationsWithTimeout:20. handler:nil]; @@ -1152,7 +1152,7 @@ - (void)testLiveTolerance }]; self.mediaPlayerController.liveTolerance = 50.; - [self.mediaPlayerController playURL:DVRTestURL()]; + [self.mediaPlayerController playURL:DVRNoTimestampTestURL()]; [self waitForExpectationsWithTimeout:20. handler:nil]; @@ -1191,7 +1191,7 @@ - (void)testMinimumDVRWindowLength }]; self.mediaPlayerController.minimumDVRWindowLength = 24. * 60. * 60.; - [self.mediaPlayerController playURL:DVRTestURL()]; + [self.mediaPlayerController playURL:DVRNoTimestampTestURL()]; [self waitForExpectationsWithTimeout:20. handler:nil]; @@ -1335,7 +1335,7 @@ - (void)testSeekNotificationForDVRStream return [notification.userInfo[SRGMediaPlayerPlaybackStateKey] integerValue] == SRGMediaPlayerPlaybackStatePlaying; }]; - [self.mediaPlayerController playURL:DVRTestURL()]; + [self.mediaPlayerController playURL:DVRNoTimestampTestURL()]; [self waitForExpectationsWithTimeout:30. handler:nil]; @@ -1359,7 +1359,7 @@ - (void)testSeekPausedDVRStreamAtEnd return [notification.userInfo[SRGMediaPlayerPlaybackStateKey] integerValue] == SRGMediaPlayerPlaybackStatePaused; }]; - [self.mediaPlayerController prepareToPlayURL:DVRTestURL() withCompletionHandler:nil]; + [self.mediaPlayerController prepareToPlayURL:DVRNoTimestampTestURL() withCompletionHandler:nil]; [self waitForExpectationsWithTimeout:30. handler:nil]; @@ -1685,7 +1685,7 @@ - (void)testDVRSeeks return [notification.userInfo[SRGMediaPlayerPlaybackStateKey] integerValue] == SRGMediaPlayerPlaybackStatePlaying; }]; - [self.mediaPlayerController playURL:DVRTestURL() atPosition:nil withSegments:nil userInfo:nil]; + [self.mediaPlayerController playURL:DVRNoTimestampTestURL() atPosition:nil withSegments:nil userInfo:nil]; [self waitForExpectationsWithTimeout:30. handler:nil]; @@ -1822,7 +1822,7 @@ - (void)testSeekToTimeBeforeDVRStreamStart return [notification.userInfo[SRGMediaPlayerPlaybackStateKey] integerValue] == SRGMediaPlayerPlaybackStatePlaying; }]; - [self.mediaPlayerController playURL:DVRTestURL() atPosition:[SRGPosition positionAtTimeInSeconds:200.] withSegments:nil userInfo:nil]; + [self.mediaPlayerController playURL:DVRNoTimestampTestURL() atPosition:[SRGPosition positionAtTimeInSeconds:200.] withSegments:nil userInfo:nil]; [self waitForExpectationsWithTimeout:30. handler:nil]; @@ -1843,7 +1843,7 @@ - (void)testSeekToTimeAfterDVRStreamEnd return [notification.userInfo[SRGMediaPlayerPlaybackStateKey] integerValue] == SRGMediaPlayerPlaybackStatePlaying; }]; - [self.mediaPlayerController playURL:DVRTestURL() atPosition:[SRGPosition positionAtTimeInSeconds:200.] withSegments:nil userInfo:nil]; + [self.mediaPlayerController playURL:DVRNoTimestampTestURL() atPosition:[SRGPosition positionAtTimeInSeconds:200.] withSegments:nil userInfo:nil]; [self waitForExpectationsWithTimeout:30. handler:nil]; @@ -1864,7 +1864,7 @@ - (void)testSeekToDateBeforeDVRStreamStart return [notification.userInfo[SRGMediaPlayerPlaybackStateKey] integerValue] == SRGMediaPlayerPlaybackStatePlaying; }]; - [self.mediaPlayerController playURL:DVRTestURL() atPosition:[SRGPosition positionAtTimeInSeconds:200.] withSegments:nil userInfo:nil]; + [self.mediaPlayerController playURL:DVRNoTimestampTestURL() atPosition:[SRGPosition positionAtTimeInSeconds:200.] withSegments:nil userInfo:nil]; [self waitForExpectationsWithTimeout:30. handler:nil]; @@ -1886,7 +1886,7 @@ - (void)testSeekToDateAfterDVRStreamEnd return [notification.userInfo[SRGMediaPlayerPlaybackStateKey] integerValue] == SRGMediaPlayerPlaybackStatePlaying; }]; - [self.mediaPlayerController playURL:DVRTestURL() atPosition:[SRGPosition positionAtTimeInSeconds:200.] withSegments:nil userInfo:nil]; + [self.mediaPlayerController playURL:DVRNoTimestampTestURL() atPosition:[SRGPosition positionAtTimeInSeconds:200.] withSegments:nil userInfo:nil]; [self waitForExpectationsWithTimeout:30. handler:nil]; @@ -1997,7 +1997,7 @@ - (void)testNoStallsDuringNormalDVRPlayback return [notification.userInfo[SRGMediaPlayerPlaybackStateKey] integerValue] == SRGMediaPlayerPlaybackStatePlaying; }]; - [self.mediaPlayerController playURL:DVRTestURL() atPosition:nil withSegments:nil userInfo:nil]; + [self.mediaPlayerController playURL:DVRNoTimestampTestURL() atPosition:nil withSegments:nil userInfo:nil]; [self waitForExpectationsWithTimeout:30. handler:nil]; @@ -2384,7 +2384,7 @@ - (void)testDVRTogglePlayPause return [notification.userInfo[SRGMediaPlayerPlaybackStateKey] integerValue] == SRGMediaPlayerPlaybackStatePlaying; }]; - [self.mediaPlayerController playURL:DVRTestURL() atPosition:nil withSegments:nil userInfo:nil]; + [self.mediaPlayerController playURL:DVRNoTimestampTestURL() atPosition:nil withSegments:nil userInfo:nil]; [self waitForExpectationsWithTimeout:30. handler:nil]; @@ -2714,7 +2714,7 @@ - (void)testDVRTimeRangeKeyValueObserving return SRG_CMTIMERANGE_IS_NOT_EMPTY(timeRangeValue.CMTimeRangeValue); }]; - [self.mediaPlayerController playURL:DVRTestURL()]; + [self.mediaPlayerController playURL:DVRNoTimestampTestURL()]; [self waitForExpectationsWithTimeout:30. handler:nil]; @@ -2772,7 +2772,7 @@ - (void)testDVRStreamTypeKeyValueObserving { [self keyValueObservingExpectationForObject:self.mediaPlayerController keyPath:@keypath(SRGMediaPlayerController.new, streamType) expectedValue:@(SRGMediaPlayerStreamTypeDVR)]; - [self.mediaPlayerController playURL:DVRTestURL()]; + [self.mediaPlayerController playURL:DVRNoTimestampTestURL()]; [self waitForExpectationsWithTimeout:30. handler:nil]; @@ -2832,7 +2832,7 @@ - (void)testDVRIsLiveKeyValueObserving [self keyValueObservingExpectationForObject:self.mediaPlayerController keyPath:@keypath(SRGMediaPlayerController.new, live) expectedValue:@YES]; self.mediaPlayerController.liveTolerance = 15.; - [self.mediaPlayerController playURL:DVRTestURL()]; + [self.mediaPlayerController playURL:DVRNoTimestampTestURL()]; [self waitForExpectationsWithTimeout:30. handler:nil]; @@ -2965,7 +2965,7 @@ - (void)testDVRStreamDateMonotony lastDate = date; }]; - [self.mediaPlayerController playURL:DVRTestURL()]; + [self.mediaPlayerController playURL:DVRNoTimestampTestURL()]; [self waitForExpectationsWithTimeout:30. handler:^(NSError * _Nullable error) { [self.mediaPlayerController removePeriodicTimeObserver:periodicTimeObserver]; @@ -3082,7 +3082,7 @@ - (void)testDVRStreamEndAbsoluteTolerance return [notification.userInfo[SRGMediaPlayerPlaybackStateKey] integerValue] == SRGMediaPlayerPlaybackStatePlaying; }]; - [self.mediaPlayerController playURL:DVRTestURL() atPosition:nil withSegments:nil userInfo:nil]; + [self.mediaPlayerController playURL:DVRNoTimestampTestURL() atPosition:nil withSegments:nil userInfo:nil]; [self waitForExpectationsWithTimeout:30. handler:nil]; diff --git a/Tests/SRGMediaPlayerTests/SegmentsTestCase.m b/Tests/SRGMediaPlayerTests/SegmentsTestCase.m index 69245f4e..e38f8e19 100644 --- a/Tests/SRGMediaPlayerTests/SegmentsTestCase.m +++ b/Tests/SRGMediaPlayerTests/SegmentsTestCase.m @@ -22,7 +22,7 @@ static NSURL *SegmentsLiveTimestampTestURL(void) { - return [NSURL URLWithString:@"https://mcdn.daserste.de/daserste/int/master.m3u8"]; + return [NSURL URLWithString:@"https://tagesschau.akamaized.net/hls/live/2020115/tagesschau/tagesschau_1/master.m3u8"]; } @interface SegmentsTestCase : MediaPlayerBaseTestCase diff --git a/Tests/SRGMediaPlayerTests/TracksTestCase.m b/Tests/SRGMediaPlayerTests/TracksTestCase.m index e0ed0598..8a6be0e1 100644 --- a/Tests/SRGMediaPlayerTests/TracksTestCase.m +++ b/Tests/SRGMediaPlayerTests/TracksTestCase.m @@ -12,7 +12,7 @@ static NSURL *SwissTracksOnDemandTestURL(void) { - return [NSURL URLWithString:@"https://rts-vod-amd.akamaized.net/ww/hls/8806923/ead53ddc-4703-35bd-b6a2-c0a9231a4d62/master.m3u8"]; + return [NSURL URLWithString:@"https://rts-vod-amd.akamaized.net/ww/8806923/f896dc42-b777-387e-9767-9e8821b502e9/master.m3u8"]; } static NSURL *InternationalTracksOnDemandTestURL(void) diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 76cb2799..d828f1b3 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -9,7 +9,7 @@ # All lines starting with a # are ignored when running `fastlane` # This is the minimum version number required. -fastlane_version '2.96.0' +fastlane_version '2.201.0' default_platform :ios @@ -25,12 +25,9 @@ platform :ios do clean_result_files clean_derived_data - Device = Struct.new(:platform, :name) - TestBuild = Struct.new(:scheme, :scheme_suffix, :in_workspace) - - iphone11 = Device.new('iOS', 'iPhone 11') + iphone13 = Device.new('iOS', 'iPhone 13') appletv = Device.new('tvOS', 'Apple TV') - devices = [iphone11, appletv] + devices = [iphone13, appletv] scheme = swift_package_name swift_package_tests = TestBuild.new(scheme, '', false) @@ -40,8 +37,7 @@ platform :ios do srg_run_tests(devices, test_builds) # Produce JUnit files for CI - srg_trainer - override_junit_test_suite_names(test_builds) + srg_junit_files end after_all do @@ -54,6 +50,9 @@ platform :ios do end end +Device = Struct.new :platform, :name +TestBuild = Struct.new :scheme, :scheme_suffix, :in_workspace + def swift_package_name JSON.parse((sh 'swift package dump-package'))['name'] end @@ -70,13 +69,11 @@ end def srg_run_tests(devices, test_builds) devices.each do |device| test_builds.each do |test_build| - begin - srg_xcodebuild(device, test_build) - rescue StandardError => e - raise e unless e.message.include? '** TEST FAILED **' + srg_xcodebuild(device, test_build) + rescue StandardError => e + raise e unless e.message.include? '** TEST FAILED **' - UI.important('One or more tests failed on ' + device.platform + ' (' + srg_xcodebuild_scheme(test_build) + '). ⚠️') - end + UI.important("One or more tests failed on #{device.platform} (#{srg_xcodebuild_scheme(test_build)}). ⚠️") end end end @@ -84,6 +81,7 @@ end def srg_xcodebuild(device, test_build) xcodebuild( test: true, + xcargs: srg_test_xcargs, workspace: srg_xcodebuild_workspace(test_build), scheme: srg_xcodebuild_scheme(test_build), destination: srg_xcodebuild_destination(device), @@ -92,8 +90,12 @@ def srg_xcodebuild(device, test_build) ) end +def srg_test_xcargs + '-retry-tests-on-failure' +end + def srg_xcodebuild_workspace(test_build) - test_build.in_workspace ? 'Tests/' + srg_xcodebuild_scheme(test_build) + '.xcworkspace' : nil + test_build.in_workspace ? "Tests/#{srg_xcodebuild_scheme(test_build)}.xcworkspace" : nil end def srg_xcodebuild_scheme(test_build) @@ -101,22 +103,30 @@ def srg_xcodebuild_scheme(test_build) end def srg_xcodebuild_destination(device) - 'platform=' + device.platform + ' Simulator,name=' + device.name + "platform=#{device.platform} Simulator,name=#{device.name}" end def srg_xcodebuild_result_bundle_path(device, test_build) - result_bundle_folder_path + test_build.scheme + '-' + device.platform + "#{result_bundle_folder_path}#{test_build.scheme}-#{device.platform}" end def srg_xcodebuild_derived_data_path '.build/DerivedData' end +def srg_junit_files + srg_trainer + override_junit_test_suite_names + expose_junit_files +end + # Convert xcresults to JUnit files def srg_trainer trainer( path: result_bundle_folder_path, output_directory: './fastlane', + extension: raw_extension, + output_remove_retry_attempts: true, fail_build: false ) end @@ -125,21 +135,31 @@ def result_bundle_folder_path './fastlane/xcresult/' end +def raw_extension + '.rawjunit' +end + # Override JUnit test suite names to split iOS and tvOS test results -def override_junit_test_suite_names(test_builds) - test_builds.each do |test_build| - Dir[test_build.scheme + '-*.xml'].each do |file_name| - override_junit_test_suite_name(file_name) - end +def override_junit_test_suite_names + Dir["*#{raw_extension}"].each do |file_name| + override_junit_test_suite_name(file_name) end end def override_junit_test_suite_name(file_name) platform = file_name.split('.').first.split('-').last file = File.open(file_name, 'r') - xml = file.read.gsub('Tests" tests="', '-' + platform + '" tests="') - xml = xml.gsub('-tests" tests="', '-' + platform + '" tests="') - File.open(file_name, 'w') { |f| f.write(xml) } + xml = file.read.gsub('Tests" tests="', "-#{platform}\" tests=\"") + xml = xml.gsub('-tests" tests="', "-#{platform}\" tests=\"") + File.write(file_name, xml) +end + +def expose_junit_files + Dir["*#{raw_extension}"].each do |file_name| + # Rename JUnit files with correct extension + new_file_name = file_name.gsub(raw_extension, '.xml') + File.rename(file_name, new_file_name) unless File.exist?(new_file_name) + end end # More information about multiple platforms in fastlane: https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Platforms.md diff --git a/fastlane/Pluginfile b/fastlane/Pluginfile deleted file mode 100644 index 72dfbbd6..00000000 --- a/fastlane/Pluginfile +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -# Autogenerated by fastlane -# -# Ensure this file is checked in to source control! - -gem 'fastlane-plugin-trainer' diff --git a/fastlane/README.md b/fastlane/README.md index 45a7da73..d812e808 100644 --- a/fastlane/README.md +++ b/fastlane/README.md @@ -1,29 +1,32 @@ fastlane documentation -================ +---- + # Installation Make sure you have the latest version of the Xcode command line tools installed: -``` +```sh xcode-select --install ``` -Install _fastlane_ using -``` -[sudo] gem install fastlane -NV -``` -or alternatively using `brew install fastlane` +For _fastlane_ installation instructions, see [Installing _fastlane_](https://docs.fastlane.tools/#installing-fastlane) # Available Actions + ## iOS + ### ios tests + +```sh +[bundle exec] fastlane ios tests ``` -fastlane ios tests -``` + Run library tests ---- This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run. -More information about fastlane can be found on [fastlane.tools](https://fastlane.tools). -The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools). + +More information about _fastlane_ can be found on [fastlane.tools](https://fastlane.tools). + +The documentation of _fastlane_ can be found on [docs.fastlane.tools](https://docs.fastlane.tools).