Commit be03c084 by andrewlewis Committed by Oliver Woodman

Add test for becoming noisy handling

To trigger receiving the broadcast it's necessary to idle() the shadow
main looper, which has to be done from the test thread. Therefore this
change removes the send broadcast action and instead sends the broadcast
from the test thread.

PiperOrigin-RevId: 279660935
parent 266c1391
......@@ -21,9 +21,11 @@ import static org.junit.Assert.fail;
import static org.robolectric.Shadows.shadowOf;
import android.content.Context;
import android.content.Intent;
import android.graphics.SurfaceTexture;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Looper;
import android.view.Surface;
import androidx.annotation.Nullable;
import androidx.test.core.app.ApplicationProvider;
......@@ -3014,6 +3016,69 @@ public final class ExoPlayerTest {
assertThat(positionMs[0]).isAtLeast(5000L);
}
@Test
public void becomingNoisyIgnoredIfBecomingNoisyHandlingIsDisabled() throws Exception {
CountDownLatch becomingNoisyHandlingDisabled = new CountDownLatch(1);
CountDownLatch becomingNoisyDelivered = new CountDownLatch(1);
PlayerStateGrabber playerStateGrabber = new PlayerStateGrabber();
ActionSchedule actionSchedule =
new ActionSchedule.Builder("becomingNoisyIgnoredIfBecomingNoisyHandlingIsDisabled")
.executeRunnable(
new PlayerRunnable() {
@Override
public void run(SimpleExoPlayer player) {
player.setHandleAudioBecomingNoisy(false);
becomingNoisyHandlingDisabled.countDown();
// Wait for the broadcast to be delivered from the main thread.
try {
becomingNoisyDelivered.await();
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
}
})
.delay(1) // Handle pending messages on the playback thread.
.executeRunnable(playerStateGrabber)
.build();
ExoPlayerTestRunner testRunner =
new ExoPlayerTestRunner.Builder().setActionSchedule(actionSchedule).build(context).start();
becomingNoisyHandlingDisabled.await();
deliverBroadcast(new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY));
becomingNoisyDelivered.countDown();
testRunner.blockUntilActionScheduleFinished(TIMEOUT_MS).blockUntilEnded(TIMEOUT_MS);
assertThat(playerStateGrabber.playWhenReady).isTrue();
}
@Test
public void pausesWhenBecomingNoisyIfBecomingNoisyHandlingIsEnabled() throws Exception {
CountDownLatch becomingNoisyHandlingEnabled = new CountDownLatch(1);
ActionSchedule actionSchedule =
new ActionSchedule.Builder("pausesWhenBecomingNoisyIfBecomingNoisyHandlingIsEnabled")
.executeRunnable(
new PlayerRunnable() {
@Override
public void run(SimpleExoPlayer player) {
player.setHandleAudioBecomingNoisy(true);
becomingNoisyHandlingEnabled.countDown();
}
})
.waitForPlayWhenReady(false) // Becoming noisy should set playWhenReady = false
.play()
.build();
ExoPlayerTestRunner testRunner =
new ExoPlayerTestRunner.Builder().setActionSchedule(actionSchedule).build(context).start();
becomingNoisyHandlingEnabled.await();
deliverBroadcast(new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY));
// If the player fails to handle becoming noisy, blockUntilActionScheduleFinished will time out
// and throw, causing the test to fail.
testRunner.blockUntilActionScheduleFinished(TIMEOUT_MS).blockUntilEnded(TIMEOUT_MS);
}
// Internal methods.
private static ActionSchedule.Builder addSurfaceSwitch(ActionSchedule.Builder builder) {
......@@ -3036,6 +3101,11 @@ public final class ExoPlayerTest {
});
}
private static void deliverBroadcast(Intent intent) {
ApplicationProvider.getApplicationContext().sendBroadcast(intent);
shadowOf(Looper.getMainLooper()).idle();
}
// Internal classes.
private static final class PositionGrabbingMessageTarget extends PlayerTarget {
......
......@@ -15,11 +15,9 @@
*/
package com.google.android.exoplayer2.testutil;
import android.content.Intent;
import android.os.Handler;
import android.view.Surface;
import androidx.annotation.Nullable;
import androidx.test.core.app.ApplicationProvider;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
......@@ -213,26 +211,6 @@ public abstract class Action {
}
/** Broadcasts an {@link Intent}. */
public static final class SendBroadcast extends Action {
private final Intent intent;
/**
* @param tag A tag to use for logging.
* @param intent The {@link Intent} to broadcast.
*/
public SendBroadcast(String tag, Intent intent) {
super(tag, "SendBroadcast: " + intent.getAction());
this.intent = intent;
}
@Override
protected void doActionImpl(
SimpleExoPlayer player, DefaultTrackSelector trackSelector, Surface surface) {
ApplicationProvider.getApplicationContext().sendBroadcast(intent);
}
}
/**
* Updates the {@link Parameters} of a {@link DefaultTrackSelector} to specify whether the
* renderer at a given index should be disabled.
......@@ -651,6 +629,57 @@ public abstract class Action {
}
/**
* Waits for a specified playWhenReady value, returning either immediately or after a call to
* {@link Player.EventListener#onPlayerStateChanged(boolean, int)}.
*/
public static final class WaitForPlayWhenReady extends Action {
private final boolean targetPlayWhenReady;
/**
* @param tag A tag to use for logging.
* @param playWhenReady The playWhenReady value to wait for.
*/
public WaitForPlayWhenReady(String tag, boolean playWhenReady) {
super(tag, "WaitForPlayWhenReady");
targetPlayWhenReady = playWhenReady;
}
@Override
protected void doActionAndScheduleNextImpl(
SimpleExoPlayer player,
DefaultTrackSelector trackSelector,
Surface surface,
HandlerWrapper handler,
ActionNode nextAction) {
if (nextAction == null) {
return;
}
if (targetPlayWhenReady == player.getPlayWhenReady()) {
nextAction.schedule(player, trackSelector, surface, handler);
} else {
player.addListener(
new Player.EventListener() {
@Override
public void onPlayerStateChanged(
boolean playWhenReady, @Player.State int playbackState) {
if (targetPlayWhenReady == playWhenReady) {
player.removeListener(this);
nextAction.schedule(player, trackSelector, surface, handler);
}
}
});
}
}
@Override
protected void doActionImpl(
SimpleExoPlayer player, DefaultTrackSelector trackSelector, Surface surface) {
// Not triggered.
}
}
/**
* Waits for a specified playback state, returning either immediately or after a call to {@link
* Player.EventListener#onPlayerStateChanged(boolean, int)}.
*/
......@@ -658,7 +687,10 @@ public abstract class Action {
private final int targetPlaybackState;
/** @param tag A tag to use for logging. */
/**
* @param tag A tag to use for logging.
* @param targetPlaybackState The playback state to wait for.
*/
public WaitForPlaybackState(String tag, int targetPlaybackState) {
super(tag, "WaitForPlaybackState");
this.targetPlaybackState = targetPlaybackState;
......
......@@ -15,7 +15,6 @@
*/
package com.google.android.exoplayer2.testutil;
import android.content.Intent;
import android.os.Looper;
import android.view.Surface;
import androidx.annotation.Nullable;
......@@ -34,7 +33,6 @@ import com.google.android.exoplayer2.testutil.Action.ExecuteRunnable;
import com.google.android.exoplayer2.testutil.Action.PlayUntilPosition;
import com.google.android.exoplayer2.testutil.Action.PrepareSource;
import com.google.android.exoplayer2.testutil.Action.Seek;
import com.google.android.exoplayer2.testutil.Action.SendBroadcast;
import com.google.android.exoplayer2.testutil.Action.SendMessages;
import com.google.android.exoplayer2.testutil.Action.SetAudioAttributes;
import com.google.android.exoplayer2.testutil.Action.SetPlayWhenReady;
......@@ -46,6 +44,7 @@ import com.google.android.exoplayer2.testutil.Action.SetVideoSurface;
import com.google.android.exoplayer2.testutil.Action.Stop;
import com.google.android.exoplayer2.testutil.Action.ThrowPlaybackException;
import com.google.android.exoplayer2.testutil.Action.WaitForIsLoading;
import com.google.android.exoplayer2.testutil.Action.WaitForPlayWhenReady;
import com.google.android.exoplayer2.testutil.Action.WaitForPlaybackState;
import com.google.android.exoplayer2.testutil.Action.WaitForPositionDiscontinuity;
import com.google.android.exoplayer2.testutil.Action.WaitForSeekProcessed;
......@@ -390,16 +389,6 @@ public final class ActionSchedule {
}
/**
* Schedules broadcasting an {@link Intent}.
*
* @param intent An intent to broadcast.
* @return The builder, for convenience.
*/
public Builder sendBroadcast(Intent intent) {
return apply(new SendBroadcast(tag, intent));
}
/**
* Schedules a delay until any timeline change.
*
* @return The builder, for convenience.
......@@ -429,6 +418,16 @@ public final class ActionSchedule {
}
/**
* Schedules a delay until playWhenReady has the specified value.
*
* @param targetPlayWhenReady The target playWhenReady value.
* @return The builder, for convenience.
*/
public Builder waitForPlayWhenReady(boolean targetPlayWhenReady) {
return apply(new WaitForPlayWhenReady(tag, targetPlayWhenReady));
}
/**
* Schedules a delay until the playback state changed to the specified state.
*
* @param targetPlaybackState The target playback state.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment