Stop using SimpleExoPlayer for tests

The class is deprecated and all tests should preferably use the
non-deprecated code paths.

PiperOrigin-RevId: 428007986
This commit is contained in:
tonihei 2022-02-11 16:25:29 +00:00 committed by Ian Baker
parent 0314d14737
commit 6dae8746ad
3 changed files with 127 additions and 178 deletions

View File

@ -65,9 +65,11 @@ import static org.junit.Assert.fail;
import static org.mockito.AdditionalMatchers.not;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
@ -180,7 +182,9 @@ import org.mockito.ArgumentMatcher;
import org.mockito.ArgumentMatchers;
import org.mockito.InOrder;
import org.mockito.Mockito;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowAudioManager;
import org.robolectric.shadows.ShadowLooper;
/** Unit test for {@link ExoPlayer}. */
@RunWith(AndroidJUnit4.class)
@ -11468,6 +11472,122 @@ public final class ExoPlayerTest {
assertThat(player.getMediaMetadata()).isEqualTo(mediaMetadata);
}
@Test
@Config(sdk = Config.ALL_SDKS)
public void builder_inBackgroundThread_doesNotThrow() throws Exception {
Thread builderThread =
new Thread(
() -> new ExoPlayer.Builder(ApplicationProvider.getApplicationContext()).build());
AtomicReference<Throwable> builderThrow = new AtomicReference<>();
builderThread.setUncaughtExceptionHandler((thread, throwable) -> builderThrow.set(throwable));
builderThread.start();
builderThread.join();
assertThat(builderThrow.get()).isNull();
}
@Test
public void onPlaylistMetadataChanged_calledWhenPlaylistMetadataSet() {
ExoPlayer player =
new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext()).build();
Player.Listener playerListener = mock(Player.Listener.class);
player.addListener(playerListener);
AnalyticsListener analyticsListener = mock(AnalyticsListener.class);
player.addAnalyticsListener(analyticsListener);
MediaMetadata mediaMetadata = new MediaMetadata.Builder().setTitle("test").build();
player.setPlaylistMetadata(mediaMetadata);
verify(playerListener).onPlaylistMetadataChanged(mediaMetadata);
verify(analyticsListener).onPlaylistMetadataChanged(any(), eq(mediaMetadata));
}
@Test
public void release_triggersAllPendingEventsInAnalyticsListeners() throws Exception {
ExoPlayer player =
new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext())
.setRenderersFactory(
(handler, videoListener, audioListener, textOutput, metadataOutput) ->
new Renderer[] {new FakeVideoRenderer(handler, videoListener)})
.build();
AnalyticsListener listener = mock(AnalyticsListener.class);
player.addAnalyticsListener(listener);
// Do something that requires clean-up callbacks like decoder disabling.
player.setMediaSource(
new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.VIDEO_FORMAT));
player.prepare();
player.play();
runUntilPlaybackState(player, Player.STATE_READY);
player.release();
ShadowLooper.runMainLooperToNextTask();
verify(listener).onVideoDisabled(any(), any());
verify(listener).onPlayerReleased(any());
}
@Test
public void releaseAfterRendererEvents_triggersPendingVideoEventsInListener() throws Exception {
Surface surface = new Surface(new SurfaceTexture(/* texName= */ 0));
ExoPlayer player =
new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext())
.setRenderersFactory(
(handler, videoListener, audioListener, textOutput, metadataOutput) ->
new Renderer[] {new FakeVideoRenderer(handler, videoListener)})
.build();
Player.Listener listener = mock(Player.Listener.class);
player.addListener(listener);
player.setMediaSource(
new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.VIDEO_FORMAT));
player.setVideoSurface(surface);
player.prepare();
player.play();
runUntilPlaybackState(player, Player.STATE_READY);
player.release();
surface.release();
ShadowLooper.runMainLooperToNextTask();
verify(listener, atLeastOnce()).onEvents(any(), any()); // EventListener
verify(listener).onRenderedFirstFrame(); // VideoListener
}
@Test
public void releaseAfterVolumeChanges_triggerPendingVolumeEventInListener() throws Exception {
ExoPlayer player =
new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext()).build();
Player.Listener listener = mock(Player.Listener.class);
player.addListener(listener);
player.setVolume(0F);
player.release();
ShadowLooper.runMainLooperToNextTask();
verify(listener).onVolumeChanged(anyFloat());
}
@Test
public void releaseAfterVolumeChanges_triggerPendingDeviceVolumeEventsInListener() {
ExoPlayer player =
new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext()).build();
Player.Listener listener = mock(Player.Listener.class);
player.addListener(listener);
int deviceVolume = player.getDeviceVolume();
try {
player.setDeviceVolume(deviceVolume + 1); // No-op if at max volume.
player.setDeviceVolume(deviceVolume - 1); // No-op if at min volume.
} finally {
player.setDeviceVolume(deviceVolume); // Restore original volume.
}
player.release();
ShadowLooper.runMainLooperToNextTask();
verify(listener, atLeast(2)).onDeviceVolumeChanged(anyInt(), anyBoolean());
}
// Internal methods.
private static ActionSchedule.Builder addSurfaceSwitch(ActionSchedule.Builder builder) {
@ -11587,7 +11707,7 @@ public final class ExoPlayerTest {
@Override
public void render(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException {
super.render(positionUs, elapsedRealtimeUs);
if (sleepOnNextRender.compareAndSet(/* expect= */ true, /* update= */ false)) {
if (sleepOnNextRender.compareAndSet(/* expectedValue= */ true, /* newValue= */ false)) {
wakeupListenerReceiver.get().onSleep(WAKEUP_DEADLINE_MS);
}
}

View File

@ -1,170 +0,0 @@
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.media3.exoplayer;
import static androidx.media3.test.utils.robolectric.TestPlayerRunHelper.runUntilPlaybackState;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import android.graphics.SurfaceTexture;
import android.view.Surface;
import androidx.media3.common.MediaMetadata;
import androidx.media3.common.Player;
import androidx.media3.exoplayer.analytics.AnalyticsListener;
import androidx.media3.test.utils.ExoPlayerTestRunner;
import androidx.media3.test.utils.FakeClock;
import androidx.media3.test.utils.FakeMediaSource;
import androidx.media3.test.utils.FakeTimeline;
import androidx.media3.test.utils.FakeVideoRenderer;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLooper;
/** Unit test for {@link SimpleExoPlayer}. */
@SuppressWarnings("deprecation") // Testing deprecated type.
@RunWith(AndroidJUnit4.class)
public class SimpleExoPlayerTest {
@Test
@Config(sdk = Config.ALL_SDKS)
public void builder_inBackgroundThread_doesNotThrow() throws Exception {
Thread builderThread =
new Thread(
() -> new SimpleExoPlayer.Builder(ApplicationProvider.getApplicationContext()).build());
AtomicReference<Throwable> builderThrow = new AtomicReference<>();
builderThread.setUncaughtExceptionHandler((thread, throwable) -> builderThrow.set(throwable));
builderThread.start();
builderThread.join();
assertThat(builderThrow.get()).isNull();
}
@Test
public void onPlaylistMetadataChanged_calledWhenPlaylistMetadataSet() {
SimpleExoPlayer player =
new SimpleExoPlayer.Builder(ApplicationProvider.getApplicationContext()).build();
Player.Listener playerListener = mock(Player.Listener.class);
player.addListener(playerListener);
AnalyticsListener analyticsListener = mock(AnalyticsListener.class);
player.addAnalyticsListener(analyticsListener);
MediaMetadata mediaMetadata = new MediaMetadata.Builder().setTitle("test").build();
player.setPlaylistMetadata(mediaMetadata);
verify(playerListener).onPlaylistMetadataChanged(mediaMetadata);
verify(analyticsListener).onPlaylistMetadataChanged(any(), eq(mediaMetadata));
}
@Test
public void release_triggersAllPendingEventsInAnalyticsListeners() throws Exception {
SimpleExoPlayer player =
new SimpleExoPlayer.Builder(
ApplicationProvider.getApplicationContext(),
(handler, videoListener, audioListener, textOutput, metadataOutput) ->
new Renderer[] {new FakeVideoRenderer(handler, videoListener)})
.setClock(new FakeClock(/* isAutoAdvancing= */ true))
.build();
AnalyticsListener listener = mock(AnalyticsListener.class);
player.addAnalyticsListener(listener);
// Do something that requires clean-up callbacks like decoder disabling.
player.setMediaSource(
new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.VIDEO_FORMAT));
player.prepare();
player.play();
runUntilPlaybackState(player, Player.STATE_READY);
player.release();
ShadowLooper.runMainLooperToNextTask();
verify(listener).onVideoDisabled(any(), any());
verify(listener).onPlayerReleased(any());
}
@Test
public void releaseAfterRendererEvents_triggersPendingVideoEventsInListener() throws Exception {
Surface surface = new Surface(new SurfaceTexture(/* texName= */ 0));
SimpleExoPlayer player =
new SimpleExoPlayer.Builder(
ApplicationProvider.getApplicationContext(),
(handler, videoListener, audioListener, textOutput, metadataOutput) ->
new Renderer[] {new FakeVideoRenderer(handler, videoListener)})
.setClock(new FakeClock(/* isAutoAdvancing= */ true))
.build();
Player.Listener listener = mock(Player.Listener.class);
player.addListener(listener);
player.setMediaSource(
new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.VIDEO_FORMAT));
player.setVideoSurface(surface);
player.prepare();
player.play();
runUntilPlaybackState(player, Player.STATE_READY);
player.release();
surface.release();
ShadowLooper.runMainLooperToNextTask();
verify(listener, atLeastOnce()).onEvents(any(), any()); // EventListener
verify(listener).onRenderedFirstFrame(); // VideoListener
}
@Test
public void releaseAfterVolumeChanges_triggerPendingVolumeEventInListener() throws Exception {
SimpleExoPlayer player =
new SimpleExoPlayer.Builder(ApplicationProvider.getApplicationContext()).build();
Player.Listener listener = mock(Player.Listener.class);
player.addListener(listener);
player.setVolume(0F);
player.release();
ShadowLooper.runMainLooperToNextTask();
verify(listener).onVolumeChanged(anyFloat());
}
@Test
public void releaseAfterVolumeChanges_triggerPendingDeviceVolumeEventsInListener() {
SimpleExoPlayer player =
new SimpleExoPlayer.Builder(ApplicationProvider.getApplicationContext()).build();
Player.Listener listener = mock(Player.Listener.class);
player.addListener(listener);
int deviceVolume = player.getDeviceVolume();
try {
player.setDeviceVolume(deviceVolume + 1); // No-op if at max volume.
player.setDeviceVolume(deviceVolume - 1); // No-op if at min volume.
} finally {
player.setDeviceVolume(deviceVolume); // Restore original volume.
}
player.release();
ShadowLooper.runMainLooperToNextTask();
verify(listener, atLeast(2)).onDeviceVolumeChanged(anyInt(), anyBoolean());
}
}

View File

@ -25,10 +25,10 @@ import androidx.media3.common.util.Assertions;
import androidx.media3.common.util.Clock;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.exoplayer.DefaultLoadControl;
import androidx.media3.exoplayer.ExoPlayer;
import androidx.media3.exoplayer.LoadControl;
import androidx.media3.exoplayer.Renderer;
import androidx.media3.exoplayer.RenderersFactory;
import androidx.media3.exoplayer.SimpleExoPlayer;
import androidx.media3.exoplayer.analytics.DefaultAnalyticsCollector;
import androidx.media3.exoplayer.source.MediaSource;
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector;
@ -36,8 +36,7 @@ import androidx.media3.exoplayer.upstream.BandwidthMeter;
import androidx.media3.exoplayer.upstream.DefaultBandwidthMeter;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/** A builder of {@link SimpleExoPlayer} instances for testing. */
@SuppressWarnings("deprecation") // Returning deprecated type for backwards compatibility.
/** A builder of {@link ExoPlayer} instances for testing. */
@UnstableApi
public class TestExoPlayerBuilder {
@ -275,8 +274,8 @@ public class TestExoPlayerBuilder {
return seekForwardIncrementMs;
}
/** Builds an {@link SimpleExoPlayer} using the provided values or their defaults. */
public SimpleExoPlayer build() {
/** Builds an {@link ExoPlayer} using the provided values or their defaults. */
public ExoPlayer build() {
Assertions.checkNotNull(
looper, "TestExoPlayer builder run on a thread without Looper and no Looper specified.");
// Do not update renderersFactory and renderers here, otherwise their getters may
@ -297,8 +296,8 @@ public class TestExoPlayerBuilder {
};
}
SimpleExoPlayer.Builder builder =
new SimpleExoPlayer.Builder(context, playerRenderersFactory)
ExoPlayer.Builder builder =
new ExoPlayer.Builder(context, playerRenderersFactory)
.setTrackSelector(trackSelector)
.setLoadControl(loadControl)
.setBandwidthMeter(bandwidthMeter)