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());
})
.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();

View File

@ -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();

View File

@ -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.
*
* <p>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.
*
* <p>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.
*
* <p>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.
*
* <p>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<Timeline> 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.
*
* <p>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<ExoPlaybackException> 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.
*
* <p>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.
*
* <p>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.
*
* <p>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}.
*
* <p>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}.
*
* <p>If a playback error occurs it will be thrown wrapped in an {@link IllegalStateException}.
*
* @param player The {@link Player}.
* @param windowIndex The window.