diff --git a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java index 0043cb9e74..66557753f4 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java @@ -17,6 +17,7 @@ package com.google.android.exoplayer2; import static com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem.END_OF_STREAM_ITEM; import static com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem.oneByteSample; +import static com.google.android.exoplayer2.testutil.TestExoPlayer.playUntilStartOfWindow; import static com.google.android.exoplayer2.testutil.TestExoPlayer.runUntilPlaybackState; import static com.google.android.exoplayer2.testutil.TestExoPlayer.runUntilTimelineChanged; import static com.google.android.exoplayer2.testutil.TestUtil.runMainLooperUntil; @@ -114,9 +115,11 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatcher; import org.mockito.InOrder; import org.mockito.Mockito; @@ -439,49 +442,41 @@ public final class ExoPlayerTest { public void repeatModeChanges() throws Exception { Timeline timeline = new FakeTimeline(/* windowCount= */ 3); FakeRenderer renderer = new FakeRenderer(C.TRACK_TYPE_VIDEO); - ActionSchedule actionSchedule = - new ActionSchedule.Builder(TAG) - .pause() - .waitForTimelineChanged( - timeline, /* expectedReason */ Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE) - .playUntilStartOfWindow(/* windowIndex= */ 1) - .setRepeatMode(Player.REPEAT_MODE_ONE) - .playUntilStartOfWindow(/* windowIndex= */ 1) - .setRepeatMode(Player.REPEAT_MODE_OFF) - .playUntilStartOfWindow(/* windowIndex= */ 2) - .setRepeatMode(Player.REPEAT_MODE_ONE) - .playUntilStartOfWindow(/* windowIndex= */ 2) - .setRepeatMode(Player.REPEAT_MODE_ALL) - .playUntilStartOfWindow(/* windowIndex= */ 0) - .setRepeatMode(Player.REPEAT_MODE_ONE) - .playUntilStartOfWindow(/* windowIndex= */ 0) - .playUntilStartOfWindow(/* windowIndex= */ 0) - .setRepeatMode(Player.REPEAT_MODE_OFF) - .play() - .build(); - ExoPlayerTestRunner testRunner = - new ExoPlayerTestRunner.Builder(context) - .setTimeline(timeline) - .setRenderers(renderer) - .setActionSchedule(actionSchedule) - .build() - .start() - .blockUntilEnded(TIMEOUT_MS); - testRunner.assertPlayedPeriodIndices(0, 1, 1, 2, 2, 0, 0, 0, 1, 2); - testRunner.assertPositionDiscontinuityReasonsEqual( - Player.DISCONTINUITY_REASON_PERIOD_TRANSITION, - Player.DISCONTINUITY_REASON_PERIOD_TRANSITION, - Player.DISCONTINUITY_REASON_PERIOD_TRANSITION, - Player.DISCONTINUITY_REASON_PERIOD_TRANSITION, - Player.DISCONTINUITY_REASON_PERIOD_TRANSITION, - Player.DISCONTINUITY_REASON_PERIOD_TRANSITION, - Player.DISCONTINUITY_REASON_PERIOD_TRANSITION, - Player.DISCONTINUITY_REASON_PERIOD_TRANSITION, - Player.DISCONTINUITY_REASON_PERIOD_TRANSITION); - testRunner.assertTimelinesSame(new FakeMediaSource.InitialTimeline(timeline), timeline); - testRunner.assertTimelineChangeReasonsEqual( - Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, - Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE); + SimpleExoPlayer player = new TestExoPlayer.Builder(context).setRenderers(renderer).build(); + AnalyticsListener mockAnalyticsListener = mock(AnalyticsListener.class); + player.addAnalyticsListener(mockAnalyticsListener); + + player.setMediaSource(new FakeMediaSource(timeline, ExoPlayerTestRunner.VIDEO_FORMAT)); + player.prepare(); + runUntilTimelineChanged(player); + playUntilStartOfWindow(player, /* windowIndex= */ 1); + player.setRepeatMode(Player.REPEAT_MODE_ONE); + playUntilStartOfWindow(player, /* windowIndex= */ 1); + player.setRepeatMode(Player.REPEAT_MODE_OFF); + playUntilStartOfWindow(player, /* windowIndex= */ 2); + player.setRepeatMode(Player.REPEAT_MODE_ONE); + playUntilStartOfWindow(player, /* windowIndex= */ 2); + player.setRepeatMode(Player.REPEAT_MODE_ALL); + playUntilStartOfWindow(player, /* windowIndex= */ 0); + player.setRepeatMode(Player.REPEAT_MODE_ONE); + playUntilStartOfWindow(player, /* windowIndex= */ 0); + playUntilStartOfWindow(player, /* windowIndex= */ 0); + player.setRepeatMode(Player.REPEAT_MODE_OFF); + playUntilStartOfWindow(player, /* windowIndex= */ 1); + playUntilStartOfWindow(player, /* windowIndex= */ 2); + player.play(); + runUntilPlaybackState(player, Player.STATE_ENDED); + + ArgumentCaptor eventTimes = + ArgumentCaptor.forClass(AnalyticsListener.EventTime.class); + verify(mockAnalyticsListener, times(10)) + .onMediaItemTransition(eventTimes.capture(), any(), anyInt()); + assertThat( + eventTimes.getAllValues().stream() + .map(eventTime -> eventTime.currentWindowIndex) + .collect(Collectors.toList())) + .containsExactly(0, 1, 1, 2, 2, 0, 0, 0, 1, 2) + .inOrder(); assertThat(renderer.isEnded).isTrue(); } diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/TestExoPlayer.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/TestExoPlayer.java index 5441d25cd9..6feea08a02 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/TestExoPlayer.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/TestExoPlayer.java @@ -20,6 +20,7 @@ import static com.google.android.exoplayer2.testutil.TestUtil.runMainLooperUntil import static com.google.common.truth.Truth.assertThat; import android.content.Context; +import android.os.Handler; import android.os.Looper; import androidx.annotation.Nullable; import com.google.android.exoplayer2.DefaultLoadControl; @@ -37,6 +38,7 @@ import com.google.android.exoplayer2.upstream.BandwidthMeter; import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Clock; +import com.google.android.exoplayer2.util.ConditionVariable; import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.video.VideoListener; import java.util.concurrent.TimeoutException; @@ -396,6 +398,7 @@ public class TestExoPlayer { */ public static void runUntilPositionDiscontinuity( Player player, @Player.DiscontinuityReason int expectedReason) throws TimeoutException { + verifyMainTestThread(player); AtomicBoolean receivedCallback = new AtomicBoolean(false); Player.EventListener listener = new Player.EventListener() { @@ -458,6 +461,59 @@ public class TestExoPlayer { runMainLooperUntil(receivedCallback::get); } + /** + * Calls {@link Player#play()}, runs tasks of the main {@link Looper} until the {@code player} + * reaches the specified position and then pauses the {@code player}. + * + * @param player The {@link Player}. + * @param windowIndex The window. + * @param positionMs The position within the window, in milliseconds. + * @throws TimeoutException If the {@link TestUtil#DEFAULT_TIMEOUT_MS default timeout} is + * exceeded. + */ + public static void playUntilPosition(ExoPlayer player, int windowIndex, long positionMs) + throws TimeoutException { + verifyMainTestThread(player); + Handler testHandler = Util.createHandlerForCurrentOrMainLooper(); + + AtomicBoolean messageHandled = new AtomicBoolean(); + player + .createMessage( + (messageType, payload) -> { + // Block playback thread until pause command has been sent from test thread. + ConditionVariable blockPlaybackThreadCondition = new ConditionVariable(); + testHandler.post( + () -> { + player.pause(); + messageHandled.set(true); + blockPlaybackThreadCondition.open(); + }); + try { + blockPlaybackThreadCondition.block(); + } catch (InterruptedException e) { + // Ignore. + } + }) + .setPosition(windowIndex, positionMs) + .send(); + player.play(); + runMainLooperUntil(messageHandled::get); + } + + /** + * Calls {@link Player#play()}, runs tasks of the main {@link Looper} until the {@code player} + * reaches the specified window and then pauses the {@code player}. + * + * @param player The {@link Player}. + * @param windowIndex The window. + * @throws TimeoutException If the {@link TestUtil#DEFAULT_TIMEOUT_MS default timeout} is + * exceeded. + */ + public static void playUntilStartOfWindow(ExoPlayer player, int windowIndex) + throws TimeoutException { + playUntilPosition(player, windowIndex, /* positionMs= */ 0); + } + /** * Runs tasks of the main {@link Looper} until the player completely handled all previously issued * commands on the internal playback thread.