-
Notifications
You must be signed in to change notification settings - Fork 284
Fix skip last calculation at increased playback speeds #5230
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
sztomek
wants to merge
7
commits into
main
Choose a base branch
from
fix/remaining-time-at-increased-speed
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+207
−22
Open
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
4a925b6
Fix skip last calculation on increased playback speeds
sztomek a495d50
Add tests
sztomek be09e04
Update changelog
sztomek 7961dc8
Address PR comments
sztomek 4251180
Merge branch 'main' into fix/remaining-time-at-increased-speed
sztomek 55d358f
Merge remote-tracking branch 'origin/main' into fix/remaining-time-at…
sztomek 5907f0b
Address PR review comments
sztomek File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
162 changes: 162 additions & 0 deletions
162
.../java/au/com/shiftyjelly/pocketcasts/repositories/playback/PlaybackManagerSkipLastTest.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,162 @@ | ||
| package au.com.shiftyjelly.pocketcasts.repositories.playback | ||
|
|
||
| import au.com.shiftyjelly.pocketcasts.repositories.playback.PlaybackManager.Companion.effectiveSkipLastMs | ||
| import au.com.shiftyjelly.pocketcasts.repositories.playback.PlaybackManager.Companion.shouldSkipLast | ||
| import org.junit.Assert.assertEquals | ||
| import org.junit.Assert.assertFalse | ||
| import org.junit.Assert.assertTrue | ||
| import org.junit.Test | ||
|
|
||
| class PlaybackManagerSkipLastTest { | ||
|
|
||
| @Test | ||
| fun `effectiveSkipLastMs at 1x equals configured seconds`() { | ||
| assertEquals(120_000L, effectiveSkipLastMs(skipLastSecs = 120, playbackSpeed = 1.0)) | ||
| } | ||
|
|
||
| @Test | ||
| fun `effectiveSkipLastMs at 2x doubles the window`() { | ||
| assertEquals(240_000L, effectiveSkipLastMs(skipLastSecs = 120, playbackSpeed = 2.0)) | ||
| } | ||
|
|
||
| @Test | ||
| fun `effectiveSkipLastMs at 1 point 5x scales linearly`() { | ||
| assertEquals(180_000L, effectiveSkipLastMs(skipLastSecs = 120, playbackSpeed = 1.5)) | ||
| } | ||
|
|
||
| @Test | ||
| fun `effectiveSkipLastMs below 1x is clamped to 1x`() { | ||
| // Sub-1x users must never lose time they configured; the threshold stays at the 1x window. | ||
| assertEquals(120_000L, effectiveSkipLastMs(skipLastSecs = 120, playbackSpeed = 0.5)) | ||
| assertEquals(120_000L, effectiveSkipLastMs(skipLastSecs = 120, playbackSpeed = 0.8)) | ||
| } | ||
|
|
||
| @Test | ||
| fun `effectiveSkipLastMs treats NaN speed as 1x`() { | ||
| assertEquals(120_000L, effectiveSkipLastMs(skipLastSecs = 120, playbackSpeed = Double.NaN)) | ||
| } | ||
|
|
||
| @Test | ||
| fun `effectiveSkipLastMs is zero when skipLastSecs is null or zero`() { | ||
| assertEquals(0L, effectiveSkipLastMs(skipLastSecs = null, playbackSpeed = 2.0)) | ||
| assertEquals(0L, effectiveSkipLastMs(skipLastSecs = 0, playbackSpeed = 2.0)) | ||
| } | ||
|
|
||
| @Test | ||
| fun `shouldSkipLast at 1x does not fire just before the window starts`() { | ||
| // 30m media, skipLast=120s. Window starts at 28:00 (1_680_000ms). One ms before → no fire. | ||
| assertFalse( | ||
| shouldSkipLast( | ||
| skipLastSecs = 120, | ||
| playbackSpeed = 1.0, | ||
| positionMs = 1_679_999, | ||
| durationMs = 1_800_000, | ||
| ), | ||
| ) | ||
| } | ||
|
|
||
| @Test | ||
| fun `shouldSkipLast at 1x fires when entering the window`() { | ||
| // 30m media, skipLast=120s. Remaining = 119_999ms < 120_000ms → fire. | ||
| assertTrue( | ||
| shouldSkipLast( | ||
| skipLastSecs = 120, | ||
| playbackSpeed = 1.0, | ||
| positionMs = 1_680_001, | ||
| durationMs = 1_800_000, | ||
| ), | ||
| ) | ||
| } | ||
|
|
||
| @Test | ||
| fun `shouldSkipLast at 2x does not fire just before the listening-time window`() { | ||
| // 30m media at 2x with skipLast=120s listening time = 240s media window. | ||
| // Before 26:00 media = still more than 4 listening minutes left, so no fire. | ||
| assertFalse( | ||
| shouldSkipLast( | ||
| skipLastSecs = 120, | ||
| playbackSpeed = 2.0, | ||
| positionMs = 1_559_999, | ||
| durationMs = 1_800_000, | ||
| ), | ||
| ) | ||
| } | ||
|
|
||
| @Test | ||
| fun `shouldSkipLast at 2x fires when 2 listening minutes remain`() { | ||
| // 30m media at 2x with skipLast=120s listening time = 240s media window. | ||
| // Remaining = 239_999ms (< 240_000ms threshold) → fire. | ||
| assertTrue( | ||
| shouldSkipLast( | ||
| skipLastSecs = 120, | ||
| playbackSpeed = 2.0, | ||
| positionMs = 1_560_001, | ||
| durationMs = 1_800_000, | ||
| ), | ||
| ) | ||
| } | ||
|
|
||
| @Test | ||
| fun `shouldSkipLast at 0 point 5x keeps the 1x window (never shrinks)`() { | ||
| // The user set Skip Last=120s. On 0.5x we must not reduce the media window below 120_000ms. | ||
| // At 120_001ms remaining (position 1_679_999), 1x would NOT fire, so neither should 0.5x. | ||
| assertFalse( | ||
| shouldSkipLast( | ||
| skipLastSecs = 120, | ||
| playbackSpeed = 0.5, | ||
| positionMs = 1_679_999, | ||
| durationMs = 1_800_000, | ||
|
sztomek marked this conversation as resolved.
|
||
| ), | ||
| ) | ||
| assertTrue( | ||
| shouldSkipLast( | ||
| skipLastSecs = 120, | ||
| playbackSpeed = 0.5, | ||
| positionMs = 1_680_001, | ||
| durationMs = 1_800_000, | ||
| ), | ||
| ) | ||
| } | ||
|
|
||
| @Test | ||
| fun `shouldSkipLast returns false when skipLastSecs is zero or null`() { | ||
| assertFalse( | ||
| shouldSkipLast(skipLastSecs = 0, playbackSpeed = 2.0, positionMs = 1_790_000, durationMs = 1_800_000), | ||
| ) | ||
| assertFalse( | ||
| shouldSkipLast(skipLastSecs = null, playbackSpeed = 2.0, positionMs = 1_790_000, durationMs = 1_800_000), | ||
| ) | ||
| } | ||
|
|
||
| @Test | ||
| fun `shouldSkipLast returns false when durationMs is null or non-positive`() { | ||
| assertFalse( | ||
| shouldSkipLast(skipLastSecs = 120, playbackSpeed = 1.0, positionMs = 1_000, durationMs = null), | ||
| ) | ||
| assertFalse( | ||
| shouldSkipLast(skipLastSecs = 120, playbackSpeed = 1.0, positionMs = 1_000, durationMs = 0), | ||
| ) | ||
| } | ||
|
|
||
| @Test | ||
| fun `shouldSkipLast returns false when positionMs is negative`() { | ||
| // Player hasn't reported a position yet. | ||
| assertFalse( | ||
| shouldSkipLast(skipLastSecs = 120, playbackSpeed = 1.0, positionMs = -1, durationMs = 1_800_000), | ||
| ) | ||
| } | ||
|
|
||
| @Test | ||
| fun `shouldSkipLast returns false when episode is shorter than the skip-last window`() { | ||
| // A 60s episode with skipLast=120s at 1x: the whole episode is inside the window, but we | ||
| // intentionally do NOT fire because the guard `durationMs > thresholdMs` filters it out | ||
| // to avoid skipping an entire short episode. | ||
| assertFalse( | ||
| shouldSkipLast(skipLastSecs = 120, playbackSpeed = 1.0, positionMs = 0, durationMs = 60_000), | ||
| ) | ||
| // Same at 2x: 120s episode, 240s window → still filtered out. | ||
| assertFalse( | ||
| shouldSkipLast(skipLastSecs = 120, playbackSpeed = 2.0, positionMs = 0, durationMs = 120_000), | ||
| ) | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.