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 ea3080a49e..bf8ce6aa6f 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 @@ -1715,7 +1715,7 @@ public final class ExoPlayerTest { throw ExoPlaybackException.createForSource(new IOException()); }) .send(); - runUntilPlaybackState(player, Player.STATE_IDLE); + TestPlayerRunHelper.runUntilError(player); player.seekTo(/* positionMs= */ 50); runUntilPendingCommandsAreFullyHandled(player); long positionAfterSeekHandled = player.getCurrentPosition(); @@ -9426,7 +9426,7 @@ public final class ExoPlayerTest { TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_READY); player.play(); player.setMediaItem(MediaItem.fromUri("http://this-will-throw-an-exception.mp4")); - TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_IDLE); + TestPlayerRunHelper.runUntilError(player); runUntilPendingCommandsAreFullyHandled(player); player.release(); diff --git a/library/core/src/test/java/com/google/android/exoplayer2/analytics/AnalyticsCollectorTest.java b/library/core/src/test/java/com/google/android/exoplayer2/analytics/AnalyticsCollectorTest.java index 06e7d8445f..635b21f6f7 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/analytics/AnalyticsCollectorTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/analytics/AnalyticsCollectorTest.java @@ -1662,7 +1662,7 @@ public final class AnalyticsCollectorTest { TestPlayerRunHelper.runUntilPositionDiscontinuity( player, Player.DISCONTINUITY_REASON_AUTO_TRANSITION); player.setMediaItem(MediaItem.fromUri("http://this-will-throw-an-exception.mp4")); - TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_IDLE); + TestPlayerRunHelper.runUntilError(player); ShadowLooper.runMainLooperToNextTask(); player.release(); surface.release(); diff --git a/robolectricutils/src/main/java/com/google/android/exoplayer2/robolectric/TestPlayerRunHelper.java b/robolectricutils/src/main/java/com/google/android/exoplayer2/robolectric/TestPlayerRunHelper.java index 50ad8b475c..a58af6b55b 100644 --- a/robolectricutils/src/main/java/com/google/android/exoplayer2/robolectric/TestPlayerRunHelper.java +++ b/robolectricutils/src/main/java/com/google/android/exoplayer2/robolectric/TestPlayerRunHelper.java @@ -17,15 +17,14 @@ package com.google.android.exoplayer2.robolectric; import static com.google.android.exoplayer2.robolectric.RobolectricUtil.runMainLooperUntil; +import static com.google.android.exoplayer2.util.Assertions.checkNotNull; import android.os.Looper; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlayer; -import com.google.android.exoplayer2.PlaybackException; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.Timeline; -import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.ConditionVariable; import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.video.VideoListener; @@ -44,7 +43,9 @@ public class TestPlayerRunHelper { /** * Runs tasks of the main {@link Looper} until {@link Player#getPlaybackState()} matches the - * expected state. + * expected state or a playback error occurs. + * + *

If a playback error occurs it will be thrown wrapped in an {@link IllegalStateException}. * * @param player The {@link Player}. * @param expectedState The expected {@link Player.State}. @@ -54,27 +55,18 @@ public class TestPlayerRunHelper { public static void runUntilPlaybackState(Player player, @Player.State int expectedState) throws TimeoutException { verifyMainTestThread(player); - if (player.getPlaybackState() == expectedState) { - return; + runMainLooperUntil( + () -> player.getPlaybackState() == expectedState || player.getPlayerError() != null); + if (player.getPlayerError() != null) { + throw new IllegalStateException(player.getPlayerError()); } - AtomicBoolean receivedExpectedState = new AtomicBoolean(false); - Player.Listener listener = - new Player.Listener() { - @Override - public void onPlaybackStateChanged(int state) { - if (state == expectedState) { - receivedExpectedState.set(true); - } - } - }; - player.addListener(listener); - runMainLooperUntil(receivedExpectedState::get); - player.removeListener(listener); } /** * Runs tasks of the main {@link Looper} until {@link Player#getPlayWhenReady()} matches the - * expected value. + * expected value or a playback error occurs. + * + *

If a playback error occurs it will be thrown wrapped in an {@link IllegalStateException}. * * @param player The {@link Player}. * @param expectedPlayWhenReady The expected value for {@link Player#getPlayWhenReady()}. @@ -84,27 +76,19 @@ public class TestPlayerRunHelper { public static void runUntilPlayWhenReady(Player player, boolean expectedPlayWhenReady) throws TimeoutException { verifyMainTestThread(player); - if (player.getPlayWhenReady() == expectedPlayWhenReady) { - return; + runMainLooperUntil( + () -> + player.getPlayWhenReady() == expectedPlayWhenReady || player.getPlayerError() != null); + if (player.getPlayerError() != null) { + throw new IllegalStateException(player.getPlayerError()); } - AtomicBoolean receivedExpectedPlayWhenReady = new AtomicBoolean(false); - Player.Listener listener = - new Player.Listener() { - @Override - public void onPlayWhenReadyChanged(boolean playWhenReady, int reason) { - if (playWhenReady == expectedPlayWhenReady) { - receivedExpectedPlayWhenReady.set(true); - } - player.removeListener(this); - } - }; - player.addListener(listener); - runMainLooperUntil(receivedExpectedPlayWhenReady::get); } /** * Runs tasks of the main {@link Looper} until {@link Player#getCurrentTimeline()} matches the - * expected timeline. + * expected timeline or a playback error occurs. + * + *

If a playback error occurs it will be thrown wrapped in an {@link IllegalStateException}. * * @param player The {@link Player}. * @param expectedTimeline The expected {@link Timeline}. @@ -114,26 +98,19 @@ public class TestPlayerRunHelper { public static void runUntilTimelineChanged(Player player, Timeline expectedTimeline) throws TimeoutException { verifyMainTestThread(player); - if (expectedTimeline.equals(player.getCurrentTimeline())) { - return; + runMainLooperUntil( + () -> + expectedTimeline.equals(player.getCurrentTimeline()) + || player.getPlayerError() != null); + if (player.getPlayerError() != null) { + throw new IllegalStateException(player.getPlayerError()); } - AtomicBoolean receivedExpectedTimeline = new AtomicBoolean(false); - Player.Listener listener = - new Player.Listener() { - @Override - public void onTimelineChanged(Timeline timeline, int reason) { - if (expectedTimeline.equals(timeline)) { - receivedExpectedTimeline.set(true); - } - player.removeListener(this); - } - }; - player.addListener(listener); - runMainLooperUntil(receivedExpectedTimeline::get); } /** - * Runs tasks of the main {@link Looper} until a timeline change occurred. + * Runs tasks of the main {@link Looper} until a timeline change or a playback error occurs. + * + *

If a playback error occurs it will be thrown wrapped in an {@link IllegalStateException}. * * @param player The {@link Player}. * @return The new {@link Timeline}. @@ -142,24 +119,29 @@ public class TestPlayerRunHelper { */ public static Timeline runUntilTimelineChanged(Player player) throws TimeoutException { verifyMainTestThread(player); - AtomicReference receivedTimeline = new AtomicReference<>(); + AtomicReference<@NullableType Timeline> receivedTimeline = new AtomicReference<>(); Player.Listener listener = new Player.Listener() { @Override public void onTimelineChanged(Timeline timeline, int reason) { receivedTimeline.set(timeline); - player.removeListener(this); } }; player.addListener(listener); - runMainLooperUntil(() -> receivedTimeline.get() != null); - return receivedTimeline.get(); + runMainLooperUntil(() -> receivedTimeline.get() != null || player.getPlayerError() != null); + player.removeListener(listener); + if (player.getPlayerError() != null) { + throw new IllegalStateException(player.getPlayerError()); + } + return checkNotNull(receivedTimeline.get()); } /** - * Runs tasks of the main {@link Looper} until a {@link - * Player.Listener#onPositionDiscontinuity(Player.PositionInfo, Player.PositionInfo, int)} - * callback with the specified {@link Player.DiscontinuityReason} occurred. + * Runs tasks of the main {@link Looper} until {@link + * Player.Listener#onPositionDiscontinuity(Player.PositionInfo, Player.PositionInfo, int)} is + * called with the specified {@link Player.DiscontinuityReason} or a playback error occurs. + * + *

If a playback error occurs it will be thrown wrapped in an {@link IllegalStateException}. * * @param player The {@link Player}. * @param expectedReason The expected {@link Player.DiscontinuityReason}. @@ -177,16 +159,19 @@ public class TestPlayerRunHelper { Player.PositionInfo oldPosition, Player.PositionInfo newPosition, int reason) { if (reason == expectedReason) { receivedCallback.set(true); - player.removeListener(this); } } }; player.addListener(listener); - runMainLooperUntil(receivedCallback::get); + runMainLooperUntil(() -> receivedCallback.get() || player.getPlayerError() != null); + player.removeListener(listener); + if (player.getPlayerError() != null) { + throw new IllegalStateException(player.getPlayerError()); + } } /** - * Runs tasks of the main {@link Looper} until a player error occurred. + * Runs tasks of the main {@link Looper} until a player error occurs. * * @param player The {@link Player}. * @return The raised {@link ExoPlaybackException}. @@ -195,25 +180,16 @@ public class TestPlayerRunHelper { */ public static ExoPlaybackException runUntilError(ExoPlayer player) throws TimeoutException { verifyMainTestThread(player); - AtomicReference receivedError = new AtomicReference<>(); - Player.Listener listener = - new Player.Listener() { - @Override - public void onPlayerError(PlaybackException error) { - // ExoPlayer is guaranteed to throw an ExoPlaybackException. - receivedError.set((ExoPlaybackException) error); - player.removeListener(this); - } - }; - player.addListener(listener); - runMainLooperUntil(() -> receivedError.get() != null); - return receivedError.get(); + runMainLooperUntil(() -> player.getPlayerError() != null); + return checkNotNull(player.getPlayerError()); } /** - * Runs tasks of the main {@link Looper} until a {@link - * ExoPlayer.AudioOffloadListener#onExperimentalOffloadSchedulingEnabledChanged} callback - * occurred. + * Runs tasks of the main {@link Looper} until {@link + * ExoPlayer.AudioOffloadListener#onExperimentalOffloadSchedulingEnabledChanged} is called or a + * playback error occurs. + * + *

If a playback error occurs it will be thrown wrapped in an {@link IllegalStateException}. * * @param player The {@link Player}. * @return The new offloadSchedulingEnabled state. @@ -234,14 +210,21 @@ public class TestPlayerRunHelper { } }; player.addAudioOffloadListener(listener); - runMainLooperUntil(() -> offloadSchedulingEnabledReceiver.get() != null); - return Assertions.checkNotNull(offloadSchedulingEnabledReceiver.get()); + runMainLooperUntil( + () -> offloadSchedulingEnabledReceiver.get() != null || player.getPlayerError() != null); + player.removeAudioOffloadListener(listener); + if (player.getPlayerError() != null) { + throw new IllegalStateException(player.getPlayerError()); + } + return checkNotNull(offloadSchedulingEnabledReceiver.get()); } /** - * Runs tasks of the main {@link Looper} until a {@link - * ExoPlayer.AudioOffloadListener#onExperimentalSleepingForOffloadChanged(boolean)} callback - * occurred. + * Runs tasks of the main {@link Looper} until {@link + * ExoPlayer.AudioOffloadListener#onExperimentalSleepingForOffloadChanged(boolean)} is called or a + * playback error occurs. + * + *

If a playback error occurs it will be thrown wrapped in an {@link IllegalStateException}. * * @param player The {@link Player}. * @param expectedSleepForOffload The expected sleep of offload state. @@ -262,12 +245,17 @@ public class TestPlayerRunHelper { } }; player.addAudioOffloadListener(listener); - runMainLooperUntil(receiverCallback::get); + runMainLooperUntil(() -> receiverCallback.get() || player.getPlayerError() != null); + if (player.getPlayerError() != null) { + throw new IllegalStateException(player.getPlayerError()); + } } /** * Runs tasks of the main {@link Looper} until the {@link VideoListener#onRenderedFirstFrame} - * callback has been called. + * callback is called or a playback error occurs. + * + *

If a playback error occurs it will be thrown wrapped in an {@link IllegalStateException}.. * * @param player The {@link Player}. * @throws TimeoutException If the {@link RobolectricUtil#DEFAULT_TIMEOUT_MS default timeout} is @@ -281,16 +269,21 @@ public class TestPlayerRunHelper { @Override public void onRenderedFirstFrame() { receivedCallback.set(true); - player.removeListener(this); } }; player.addListener(listener); - runMainLooperUntil(receivedCallback::get); + runMainLooperUntil(() -> receivedCallback.get() || player.getPlayerError() != null); + player.removeListener(listener); + if (player.getPlayerError() != null) { + throw new IllegalStateException(player.getPlayerError()); + } } /** * 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}. + * reaches the specified position or a playback error occurs, and then pauses the {@code player}. + * + *

If a playback error occurs it will be thrown wrapped in an {@link IllegalStateException}. * * @param player The {@link Player}. * @param windowIndex The window. @@ -327,12 +320,17 @@ public class TestPlayerRunHelper { .setPosition(windowIndex, positionMs) .send(); player.play(); - runMainLooperUntil(messageHandled::get); + runMainLooperUntil(() -> messageHandled.get() || player.getPlayerError() != null); + if (player.getPlayerError() != null) { + throw new IllegalStateException(player.getPlayerError()); + } } /** * 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}. + * reaches the specified window or a playback error occurs, and then pauses the {@code player}. + * + *

If a playback error occurs it will be thrown wrapped in an {@link IllegalStateException}. * * @param player The {@link Player}. * @param windowIndex The window.