Change TestPlayerRunHelper to fail-fast on playback error

If a test expects an error they can use runUntilError()

PiperOrigin-RevId: 382765060
This commit is contained in:
ibaker 2021-07-02 18:17:39 +01:00 committed by kim-vde
parent ee488e6625
commit 227ac89eff
3 changed files with 90 additions and 92 deletions

View File

@ -1715,7 +1715,7 @@ public final class ExoPlayerTest {
throw ExoPlaybackException.createForSource(new IOException()); throw ExoPlaybackException.createForSource(new IOException());
}) })
.send(); .send();
runUntilPlaybackState(player, Player.STATE_IDLE); TestPlayerRunHelper.runUntilError(player);
player.seekTo(/* positionMs= */ 50); player.seekTo(/* positionMs= */ 50);
runUntilPendingCommandsAreFullyHandled(player); runUntilPendingCommandsAreFullyHandled(player);
long positionAfterSeekHandled = player.getCurrentPosition(); long positionAfterSeekHandled = player.getCurrentPosition();
@ -9426,7 +9426,7 @@ public final class ExoPlayerTest {
TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_READY); TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_READY);
player.play(); player.play();
player.setMediaItem(MediaItem.fromUri("http://this-will-throw-an-exception.mp4")); player.setMediaItem(MediaItem.fromUri("http://this-will-throw-an-exception.mp4"));
TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_IDLE); TestPlayerRunHelper.runUntilError(player);
runUntilPendingCommandsAreFullyHandled(player); runUntilPendingCommandsAreFullyHandled(player);
player.release(); player.release();

View File

@ -1662,7 +1662,7 @@ public final class AnalyticsCollectorTest {
TestPlayerRunHelper.runUntilPositionDiscontinuity( TestPlayerRunHelper.runUntilPositionDiscontinuity(
player, Player.DISCONTINUITY_REASON_AUTO_TRANSITION); player, Player.DISCONTINUITY_REASON_AUTO_TRANSITION);
player.setMediaItem(MediaItem.fromUri("http://this-will-throw-an-exception.mp4")); player.setMediaItem(MediaItem.fromUri("http://this-will-throw-an-exception.mp4"));
TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_IDLE); TestPlayerRunHelper.runUntilError(player);
ShadowLooper.runMainLooperToNextTask(); ShadowLooper.runMainLooperToNextTask();
player.release(); player.release();
surface.release(); surface.release();

View File

@ -17,15 +17,14 @@
package com.google.android.exoplayer2.robolectric; package com.google.android.exoplayer2.robolectric;
import static com.google.android.exoplayer2.robolectric.RobolectricUtil.runMainLooperUntil; import static com.google.android.exoplayer2.robolectric.RobolectricUtil.runMainLooperUntil;
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
import android.os.Looper; import android.os.Looper;
import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.PlaybackException;
import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.Timeline; 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.ConditionVariable;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.video.VideoListener; 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 * Runs tasks of the main {@link Looper} until {@link Player#getPlaybackState()} matches the
* expected state. * expected state or a playback error occurs.
*
* <p>If a playback error occurs it will be thrown wrapped in an {@link IllegalStateException}.
* *
* @param player The {@link Player}. * @param player The {@link Player}.
* @param expectedState The expected {@link Player.State}. * @param expectedState The expected {@link Player.State}.
@ -54,27 +55,18 @@ public class TestPlayerRunHelper {
public static void runUntilPlaybackState(Player player, @Player.State int expectedState) public static void runUntilPlaybackState(Player player, @Player.State int expectedState)
throws TimeoutException { throws TimeoutException {
verifyMainTestThread(player); verifyMainTestThread(player);
if (player.getPlaybackState() == expectedState) { runMainLooperUntil(
return; () -> 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 * Runs tasks of the main {@link Looper} until {@link Player#getPlayWhenReady()} matches the
* expected value. * expected value or a playback error occurs.
*
* <p>If a playback error occurs it will be thrown wrapped in an {@link IllegalStateException}.
* *
* @param player The {@link Player}. * @param player The {@link Player}.
* @param expectedPlayWhenReady The expected value for {@link Player#getPlayWhenReady()}. * @param expectedPlayWhenReady The expected value for {@link Player#getPlayWhenReady()}.
@ -84,27 +76,19 @@ public class TestPlayerRunHelper {
public static void runUntilPlayWhenReady(Player player, boolean expectedPlayWhenReady) public static void runUntilPlayWhenReady(Player player, boolean expectedPlayWhenReady)
throws TimeoutException { throws TimeoutException {
verifyMainTestThread(player); verifyMainTestThread(player);
if (player.getPlayWhenReady() == expectedPlayWhenReady) { runMainLooperUntil(
return; () ->
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 * Runs tasks of the main {@link Looper} until {@link Player#getCurrentTimeline()} matches the
* expected timeline. * expected timeline or a playback error occurs.
*
* <p>If a playback error occurs it will be thrown wrapped in an {@link IllegalStateException}.
* *
* @param player The {@link Player}. * @param player The {@link Player}.
* @param expectedTimeline The expected {@link Timeline}. * @param expectedTimeline The expected {@link Timeline}.
@ -114,26 +98,19 @@ public class TestPlayerRunHelper {
public static void runUntilTimelineChanged(Player player, Timeline expectedTimeline) public static void runUntilTimelineChanged(Player player, Timeline expectedTimeline)
throws TimeoutException { throws TimeoutException {
verifyMainTestThread(player); verifyMainTestThread(player);
if (expectedTimeline.equals(player.getCurrentTimeline())) { runMainLooperUntil(
return; () ->
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.
*
* <p>If a playback error occurs it will be thrown wrapped in an {@link IllegalStateException}.
* *
* @param player The {@link Player}. * @param player The {@link Player}.
* @return The new {@link Timeline}. * @return The new {@link Timeline}.
@ -142,24 +119,29 @@ public class TestPlayerRunHelper {
*/ */
public static Timeline runUntilTimelineChanged(Player player) throws TimeoutException { public static Timeline runUntilTimelineChanged(Player player) throws TimeoutException {
verifyMainTestThread(player); verifyMainTestThread(player);
AtomicReference<Timeline> receivedTimeline = new AtomicReference<>(); AtomicReference<@NullableType Timeline> receivedTimeline = new AtomicReference<>();
Player.Listener listener = Player.Listener listener =
new Player.Listener() { new Player.Listener() {
@Override @Override
public void onTimelineChanged(Timeline timeline, int reason) { public void onTimelineChanged(Timeline timeline, int reason) {
receivedTimeline.set(timeline); receivedTimeline.set(timeline);
player.removeListener(this);
} }
}; };
player.addListener(listener); player.addListener(listener);
runMainLooperUntil(() -> receivedTimeline.get() != null); runMainLooperUntil(() -> receivedTimeline.get() != null || player.getPlayerError() != null);
return receivedTimeline.get(); 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 * Runs tasks of the main {@link Looper} until {@link
* Player.Listener#onPositionDiscontinuity(Player.PositionInfo, Player.PositionInfo, int)} * Player.Listener#onPositionDiscontinuity(Player.PositionInfo, Player.PositionInfo, int)} is
* callback with the specified {@link Player.DiscontinuityReason} occurred. * called with the specified {@link Player.DiscontinuityReason} or a playback error occurs.
*
* <p>If a playback error occurs it will be thrown wrapped in an {@link IllegalStateException}.
* *
* @param player The {@link Player}. * @param player The {@link Player}.
* @param expectedReason The expected {@link Player.DiscontinuityReason}. * @param expectedReason The expected {@link Player.DiscontinuityReason}.
@ -177,16 +159,19 @@ public class TestPlayerRunHelper {
Player.PositionInfo oldPosition, Player.PositionInfo newPosition, int reason) { Player.PositionInfo oldPosition, Player.PositionInfo newPosition, int reason) {
if (reason == expectedReason) { if (reason == expectedReason) {
receivedCallback.set(true); receivedCallback.set(true);
player.removeListener(this);
} }
} }
}; };
player.addListener(listener); 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}. * @param player The {@link Player}.
* @return The raised {@link ExoPlaybackException}. * @return The raised {@link ExoPlaybackException}.
@ -195,25 +180,16 @@ public class TestPlayerRunHelper {
*/ */
public static ExoPlaybackException runUntilError(ExoPlayer player) throws TimeoutException { public static ExoPlaybackException runUntilError(ExoPlayer player) throws TimeoutException {
verifyMainTestThread(player); verifyMainTestThread(player);
AtomicReference<ExoPlaybackException> receivedError = new AtomicReference<>(); runMainLooperUntil(() -> player.getPlayerError() != null);
Player.Listener listener = return checkNotNull(player.getPlayerError());
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();
} }
/** /**
* Runs tasks of the main {@link Looper} until a {@link * Runs tasks of the main {@link Looper} until {@link
* ExoPlayer.AudioOffloadListener#onExperimentalOffloadSchedulingEnabledChanged} callback * ExoPlayer.AudioOffloadListener#onExperimentalOffloadSchedulingEnabledChanged} is called or a
* occurred. * playback error occurs.
*
* <p>If a playback error occurs it will be thrown wrapped in an {@link IllegalStateException}.
* *
* @param player The {@link Player}. * @param player The {@link Player}.
* @return The new offloadSchedulingEnabled state. * @return The new offloadSchedulingEnabled state.
@ -234,14 +210,21 @@ public class TestPlayerRunHelper {
} }
}; };
player.addAudioOffloadListener(listener); player.addAudioOffloadListener(listener);
runMainLooperUntil(() -> offloadSchedulingEnabledReceiver.get() != null); runMainLooperUntil(
return Assertions.checkNotNull(offloadSchedulingEnabledReceiver.get()); () -> 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 * Runs tasks of the main {@link Looper} until {@link
* ExoPlayer.AudioOffloadListener#onExperimentalSleepingForOffloadChanged(boolean)} callback * ExoPlayer.AudioOffloadListener#onExperimentalSleepingForOffloadChanged(boolean)} is called or a
* occurred. * playback error occurs.
*
* <p>If a playback error occurs it will be thrown wrapped in an {@link IllegalStateException}.
* *
* @param player The {@link Player}. * @param player The {@link Player}.
* @param expectedSleepForOffload The expected sleep of offload state. * @param expectedSleepForOffload The expected sleep of offload state.
@ -262,12 +245,17 @@ public class TestPlayerRunHelper {
} }
}; };
player.addAudioOffloadListener(listener); 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} * Runs tasks of the main {@link Looper} until the {@link VideoListener#onRenderedFirstFrame}
* callback has been called. * callback is called or a playback error occurs.
*
* <p>If a playback error occurs it will be thrown wrapped in an {@link IllegalStateException}..
* *
* @param player The {@link Player}. * @param player The {@link Player}.
* @throws TimeoutException If the {@link RobolectricUtil#DEFAULT_TIMEOUT_MS default timeout} is * @throws TimeoutException If the {@link RobolectricUtil#DEFAULT_TIMEOUT_MS default timeout} is
@ -281,16 +269,21 @@ public class TestPlayerRunHelper {
@Override @Override
public void onRenderedFirstFrame() { public void onRenderedFirstFrame() {
receivedCallback.set(true); receivedCallback.set(true);
player.removeListener(this);
} }
}; };
player.addListener(listener); 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} * 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}.
*
* <p>If a playback error occurs it will be thrown wrapped in an {@link IllegalStateException}.
* *
* @param player The {@link Player}. * @param player The {@link Player}.
* @param windowIndex The window. * @param windowIndex The window.
@ -327,12 +320,17 @@ public class TestPlayerRunHelper {
.setPosition(windowIndex, positionMs) .setPosition(windowIndex, positionMs)
.send(); .send();
player.play(); 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} * 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}.
*
* <p>If a playback error occurs it will be thrown wrapped in an {@link IllegalStateException}.
* *
* @param player The {@link Player}. * @param player The {@link Player}.
* @param windowIndex The window. * @param windowIndex The window.