From 6dae8746ad503520bf076e9d744fbdafd7ac6a52 Mon Sep 17 00:00:00 2001 From: tonihei Date: Fri, 11 Feb 2022 16:25:29 +0000 Subject: [PATCH] Stop using SimpleExoPlayer for tests The class is deprecated and all tests should preferably use the non-deprecated code paths. PiperOrigin-RevId: 428007986 --- .../media3/exoplayer/ExoPlayerTest.java | 122 ++++++++++++- .../media3/exoplayer/SimpleExoPlayerTest.java | 170 ------------------ .../test/utils/TestExoPlayerBuilder.java | 13 +- 3 files changed, 127 insertions(+), 178 deletions(-) delete mode 100644 libraries/exoplayer/src/test/java/androidx/media3/exoplayer/SimpleExoPlayerTest.java diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java index 5b7802c49f..d99bbeeb2f 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java @@ -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 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); } } diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/SimpleExoPlayerTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/SimpleExoPlayerTest.java deleted file mode 100644 index 9a1dbb7e62..0000000000 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/SimpleExoPlayerTest.java +++ /dev/null @@ -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 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()); - } -} diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/TestExoPlayerBuilder.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/TestExoPlayerBuilder.java index 3917a4bf24..ef866067fc 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/TestExoPlayerBuilder.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/TestExoPlayerBuilder.java @@ -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)