| package org.robolectric.shadows; |
| |
| import java.io.File; |
| import java.io.FileDescriptor; |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.util.EnumSet; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| import android.content.Context; |
| import android.media.AudioManager; |
| import android.media.MediaPlayer; |
| import android.net.Uri; |
| import android.os.SystemClock; |
| |
| import org.assertj.core.api.Assertions; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.mockito.Mockito; |
| import org.robolectric.Robolectric; |
| import org.robolectric.Shadows; |
| import org.robolectric.TestRunners; |
| import org.robolectric.bytecode.RobolectricInternals; |
| import org.robolectric.internal.Shadow; |
| import org.robolectric.shadows.ShadowMediaPlayer.InvalidStateBehavior; |
| import org.robolectric.shadows.ShadowMediaPlayer.MediaInfo; |
| import org.robolectric.shadows.ShadowMediaPlayer.State; |
| import org.robolectric.util.Scheduler; |
| |
| import static org.assertj.core.api.Assertions.assertThat; |
| import static org.robolectric.shadows.ShadowMediaPlayer.State.*; |
| |
| @RunWith(TestRunners.WithDefaults.class) |
| public class ShadowMediaPlayerTest { |
| |
| private MediaPlayer mediaPlayer; |
| private ShadowMediaPlayer shadowMediaPlayer; |
| private MediaPlayer.OnCompletionListener completionListener; |
| private MediaPlayer.OnErrorListener errorListener; |
| private MediaPlayer.OnInfoListener infoListener; |
| private MediaPlayer.OnPreparedListener preparedListener; |
| private MediaPlayer.OnSeekCompleteListener seekListener; |
| private Scheduler scheduler; |
| |
| @Before |
| public void setUp() throws Exception { |
| mediaPlayer = Shadow.newInstanceOf(MediaPlayer.class); |
| shadowMediaPlayer = Shadows.shadowOf(mediaPlayer); |
| |
| completionListener = Mockito.mock(MediaPlayer.OnCompletionListener.class); |
| mediaPlayer.setOnCompletionListener(completionListener); |
| |
| preparedListener = Mockito.mock(MediaPlayer.OnPreparedListener.class); |
| mediaPlayer.setOnPreparedListener(preparedListener); |
| |
| errorListener = Mockito.mock(MediaPlayer.OnErrorListener.class); |
| mediaPlayer.setOnErrorListener(errorListener); |
| |
| infoListener = Mockito.mock(MediaPlayer.OnInfoListener.class); |
| mediaPlayer.setOnInfoListener(infoListener); |
| |
| seekListener = Mockito.mock(MediaPlayer.OnSeekCompleteListener.class); |
| mediaPlayer.setOnSeekCompleteListener(seekListener); |
| |
| // Scheduler is used in many of the tests to simulate |
| // moving forward in time. |
| scheduler = Robolectric.getUiThreadScheduler(); |
| scheduler.pause(); |
| } |
| |
| @Test |
| public void testInitialState() { |
| assertThat(shadowMediaPlayer.getState()).isEqualTo(IDLE); |
| } |
| |
| @Test |
| public void testCreateListener() { |
| ShadowMediaPlayer.CreateListener createListener = Mockito |
| .mock(ShadowMediaPlayer.CreateListener.class); |
| ShadowMediaPlayer.setCreateListener(createListener); |
| |
| MediaPlayer newPlayer = new MediaPlayer(); |
| ShadowMediaPlayer shadow = Shadows.shadowOf(newPlayer); |
| |
| Mockito.verify(createListener).onCreate(newPlayer, shadow); |
| ShadowMediaPlayer.setCreateListener(null); |
| } |
| |
| @Test |
| public void testResetResetsPosition() { |
| shadowMediaPlayer.setCurrentPosition(300); |
| mediaPlayer.reset(); |
| assertThat(shadowMediaPlayer.getCurrentPositionRaw()) |
| .isEqualTo(0); |
| } |
| |
| @Test |
| public void testPrepare() throws IOException { |
| int[] testDelays = { 0, 10, 100, 1500 }; |
| |
| for (int delay : testDelays) { |
| final long startTime = scheduler.getCurrentTime(); |
| shadowMediaPlayer.setState(INITIALIZED); |
| shadowMediaPlayer.getDefaultMediaInfo().setPreparationDelay(delay); |
| mediaPlayer.prepare(); |
| |
| assertThat(shadowMediaPlayer.getState()).isEqualTo(PREPARED); |
| assertThat(scheduler.getCurrentTime()).isEqualTo(startTime + delay); |
| } |
| } |
| |
| @Test |
| public void testSetDefaultMediaInfo() { |
| MediaInfo info = shadowMediaPlayer.buildMediaInfo(30, 300); |
| shadowMediaPlayer.setDefaultMediaInfo(info); |
| |
| assertThat(shadowMediaPlayer.getDefaultMediaInfo()).isSameAs(info); |
| } |
| |
| @Test |
| public void testSetCurrentMediaInfo() { |
| MediaInfo info = shadowMediaPlayer.buildMediaInfo(30, 300); |
| shadowMediaPlayer.setCurrentMediaInfo(info); |
| |
| assertThat(shadowMediaPlayer.getCurrentMediaInfo()).isSameAs(info); |
| } |
| |
| @Test |
| public void testSetCurrentMediaInfoDefault() { |
| MediaInfo info = shadowMediaPlayer.buildMediaInfo(30, 300); |
| shadowMediaPlayer.setCurrentMediaInfo(info); |
| shadowMediaPlayer.setCurrentMediaInfo(null); |
| |
| assertThat(shadowMediaPlayer.getCurrentMediaInfo()).isSameAs( |
| shadowMediaPlayer.getDefaultMediaInfo()); |
| } |
| |
| @Test |
| public void testDataSourceMap() throws IOException { |
| Map<String, MediaInfo> map = new HashMap<String, MediaInfo>(); |
| MediaInfo info = shadowMediaPlayer.buildMediaInfo(100, 200); |
| map.put("clip", info); |
| shadowMediaPlayer.setDataSourceMap(map); |
| mediaPlayer.setDataSource("clip"); |
| |
| assertThat(shadowMediaPlayer.getCurrentMediaInfo()).isSameAs(info); |
| } |
| |
| @Test |
| public void testSetDataSourceString() throws IOException { |
| mediaPlayer.setDataSource("dummy"); |
| |
| assertThat(shadowMediaPlayer.getSourceFD()).as("sourceFD").isNull(); |
| assertThat(shadowMediaPlayer.getSourceOffset()).as("sourceOffset") |
| .isEqualTo(-1); |
| assertThat(shadowMediaPlayer.getSourceLength()).as("sourceLength") |
| .isEqualTo(-1); |
| assertThat(shadowMediaPlayer.getSourcePath()).as("sourcePath").isEqualTo( |
| "dummy"); |
| assertThat(shadowMediaPlayer.getSourceUri()).as("sourceUri").isNull(); |
| assertThat(shadowMediaPlayer.getSourceHeaders()).as("sourceHeaders") |
| .isNull(); |
| } |
| |
| @Test |
| public void testSetDataSourceUri() throws IOException { |
| Map<String, String> headers = new HashMap<String, String>(); |
| Uri uri = Uri.parse("file:/test"); |
| mediaPlayer.setDataSource(Robolectric.application, uri, headers); |
| |
| assertThat(shadowMediaPlayer.getSourceFD()).as("sourceFD").isNull(); |
| assertThat(shadowMediaPlayer.getSourceOffset()).as("sourceOffset") |
| .isEqualTo(-1); |
| assertThat(shadowMediaPlayer.getSourceLength()).as("sourceLength") |
| .isEqualTo(-1); |
| assertThat(shadowMediaPlayer.getSourcePath()).as("sourcePath").isNull(); |
| assertThat(shadowMediaPlayer.getSourceUri()).as("sourceUri").isSameAs(uri); |
| assertThat(shadowMediaPlayer.getSourceHeaders()).as("sourceHeaders") |
| .isSameAs(headers); |
| } |
| |
| @Test |
| public void testSetDataSourceFD() throws IOException { |
| File tmpFile = File.createTempFile("MediaPlayerTest", null); |
| try { |
| tmpFile.deleteOnExit(); |
| FileInputStream is = new FileInputStream(tmpFile); |
| try { |
| FileDescriptor fd = is.getFD(); |
| mediaPlayer.setDataSource(fd, 23, 524); |
| |
| assertThat(shadowMediaPlayer.getSourceFD()).isSameAs(fd); |
| assertThat(shadowMediaPlayer.getSourceOffset()).isEqualTo(23); |
| assertThat(shadowMediaPlayer.getSourceLength()).isEqualTo(524); |
| assertThat(shadowMediaPlayer.getSourcePath()).as("sourcePath").isNull(); |
| assertThat(shadowMediaPlayer.getSourceUri()).as("sourceUri").isNull(); |
| assertThat(shadowMediaPlayer.getSourceHeaders()).as("sourceHeaders") |
| .isNull(); |
| } finally { |
| is.close(); |
| } |
| } finally { |
| tmpFile.delete(); |
| } |
| } |
| |
| @Test |
| public void testPrepareAsyncAutoCallback() { |
| mediaPlayer.setOnPreparedListener(preparedListener); |
| int[] testDelays = { 0, 10, 100, 1500 }; |
| |
| for (int delay : testDelays) { |
| shadowMediaPlayer.setState(INITIALIZED); |
| shadowMediaPlayer.getDefaultMediaInfo().setPreparationDelay(delay); |
| final long startTime = scheduler.getCurrentTime(); |
| mediaPlayer.prepareAsync(); |
| |
| assertThat(shadowMediaPlayer.getState()).isEqualTo(PREPARING); |
| Mockito.verifyZeroInteractions(preparedListener); |
| scheduler.advanceToLastPostedRunnable(); |
| assertThat(scheduler.getCurrentTime()).as("currentTime").isEqualTo( |
| startTime + delay); |
| assertThat(shadowMediaPlayer.getState()).isEqualTo(PREPARED); |
| Mockito.verify(preparedListener).onPrepared(mediaPlayer); |
| Mockito.verifyNoMoreInteractions(preparedListener); |
| Mockito.reset(preparedListener); |
| } |
| } |
| |
| @Test |
| public void testPrepareAsyncManualCallback() { |
| mediaPlayer.setOnPreparedListener(preparedListener); |
| |
| shadowMediaPlayer.setState(INITIALIZED); |
| shadowMediaPlayer.getDefaultMediaInfo().setPreparationDelay(-1); |
| final long startTime = scheduler.getCurrentTime(); |
| mediaPlayer.prepareAsync(); |
| |
| assertThat(scheduler.getCurrentTime()).as("currentTime").isEqualTo( |
| startTime); |
| assertThat(shadowMediaPlayer.getState()).isSameAs(PREPARING); |
| Mockito.verifyZeroInteractions(preparedListener); |
| shadowMediaPlayer.invokePreparedListener(); |
| assertThat(shadowMediaPlayer.getState()).isSameAs(PREPARED); |
| Mockito.verify(preparedListener).onPrepared(mediaPlayer); |
| Mockito.verifyNoMoreInteractions(preparedListener); |
| } |
| |
| @Test |
| public void testDefaultPreparationDelay() { |
| assertThat(shadowMediaPlayer.getDefaultMediaInfo().getPreparationDelay()) |
| .as("preparationDelay").isEqualTo(0); |
| } |
| |
| @Test |
| public void testIsPlaying() { |
| EnumSet<State> nonPlayingStates = EnumSet.of(IDLE, INITIALIZED, PREPARED, |
| PAUSED, STOPPED, PLAYBACK_COMPLETED); |
| for (State state : nonPlayingStates) { |
| shadowMediaPlayer.setState(state); |
| assertThat(mediaPlayer.isPlaying()).overridingErrorMessage( |
| "In state <%s>, expected isPlaying() to be false", state).isFalse(); |
| } |
| shadowMediaPlayer.setState(STARTED); |
| assertThat(mediaPlayer.isPlaying()).overridingErrorMessage( |
| "In state <STARTED>, expected isPlaying() to be true").isTrue(); |
| } |
| |
| @Test |
| public void testIsPrepared() { |
| EnumSet<State> prepStates = EnumSet.of(PREPARED, STARTED, PAUSED, |
| PLAYBACK_COMPLETED); |
| |
| for (State state : State.values()) { |
| shadowMediaPlayer.setState(state); |
| if (prepStates.contains(state)) { |
| assertThat(shadowMediaPlayer.isPrepared()).overridingErrorMessage( |
| "In state <%s>, expected isPrepared() to be true", state).isTrue(); |
| } else { |
| assertThat(shadowMediaPlayer.isPrepared()).overridingErrorMessage( |
| "In state <%s>, expected isPrepared() to be false", state) |
| .isFalse(); |
| } |
| } |
| } |
| |
| @Test |
| public void testPlaybackProgress() { |
| shadowMediaPlayer.setState(PREPARED); |
| // This time offset is just to make sure that it doesn't work by |
| // accident because the offsets are calculated relative to 0. |
| scheduler.advanceBy(100); |
| |
| mediaPlayer.start(); |
| assertThat(shadowMediaPlayer.getCurrentPosition()).isEqualTo(0); |
| assertThat(shadowMediaPlayer.getState()).isEqualTo(STARTED); |
| |
| scheduler.advanceBy(500); |
| assertThat(shadowMediaPlayer.getCurrentPosition()).isEqualTo(500); |
| assertThat(shadowMediaPlayer.getState()).isEqualTo(STARTED); |
| |
| scheduler.advanceBy(499); |
| assertThat(shadowMediaPlayer.getCurrentPosition()).isEqualTo(999); |
| assertThat(shadowMediaPlayer.getState()).isEqualTo(STARTED); |
| Mockito.verifyZeroInteractions(completionListener); |
| |
| scheduler.advanceBy(1); |
| assertThat(shadowMediaPlayer.getCurrentPosition()).isEqualTo(1000); |
| assertThat(shadowMediaPlayer.getState()).isEqualTo(PLAYBACK_COMPLETED); |
| Mockito.verify(completionListener).onCompletion(mediaPlayer); |
| Mockito.verifyNoMoreInteractions(completionListener); |
| |
| scheduler.advanceBy(1); |
| assertThat(shadowMediaPlayer.getCurrentPosition()).isEqualTo(1000); |
| assertThat(shadowMediaPlayer.getState()).isEqualTo(PLAYBACK_COMPLETED); |
| Mockito.verifyZeroInteractions(completionListener); |
| } |
| |
| @Test |
| public void testStop() { |
| shadowMediaPlayer.setState(PREPARED); |
| mediaPlayer.start(); |
| scheduler.advanceBy(300); |
| |
| mediaPlayer.stop(); |
| assertThat(mediaPlayer.getCurrentPosition()).isEqualTo(300); |
| |
| scheduler.advanceBy(400); |
| assertThat(mediaPlayer.getCurrentPosition()).isEqualTo(300); |
| } |
| |
| @Test |
| public void testPauseReschedulesCompletionCallback() { |
| shadowMediaPlayer.setState(PREPARED); |
| mediaPlayer.start(); |
| scheduler.advanceBy(200); |
| mediaPlayer.pause(); |
| scheduler.advanceBy(800); |
| |
| Mockito.verifyZeroInteractions(completionListener); |
| |
| mediaPlayer.start(); |
| scheduler.advanceBy(799); |
| Mockito.verifyZeroInteractions(completionListener); |
| |
| scheduler.advanceBy(1); |
| Mockito.verify(completionListener).onCompletion(mediaPlayer); |
| Mockito.verifyNoMoreInteractions(completionListener); |
| |
| assertThat(scheduler.advanceToLastPostedRunnable()).isFalse(); |
| Mockito.verifyZeroInteractions(completionListener); |
| } |
| |
| @Test |
| public void testPauseUpdatesPosition() { |
| shadowMediaPlayer.setState(PREPARED); |
| mediaPlayer.start(); |
| |
| scheduler.advanceBy(200); |
| mediaPlayer.pause(); |
| scheduler.advanceBy(200); |
| |
| assertThat(shadowMediaPlayer.getState()).isEqualTo(PAUSED); |
| assertThat(shadowMediaPlayer.getCurrentPosition()).isEqualTo(200); |
| |
| mediaPlayer.start(); |
| scheduler.advanceBy(200); |
| |
| assertThat(shadowMediaPlayer.getState()).isEqualTo(STARTED); |
| assertThat(shadowMediaPlayer.getCurrentPosition()).isEqualTo(400); |
| } |
| |
| @Test |
| public void testSeekDuringPlaybackReschedulesCompletionCallback() { |
| shadowMediaPlayer.setState(PREPARED); |
| mediaPlayer.start(); |
| |
| scheduler.advanceBy(300); |
| mediaPlayer.seekTo(400); |
| scheduler.advanceBy(599); |
| Mockito.verifyZeroInteractions(completionListener); |
| scheduler.advanceBy(1); |
| Mockito.verify(completionListener).onCompletion(mediaPlayer); |
| Mockito.verifyNoMoreInteractions(completionListener); |
| assertThat(shadowMediaPlayer.getState()).isEqualTo(PLAYBACK_COMPLETED); |
| |
| assertThat(scheduler.advanceToLastPostedRunnable()).isFalse(); |
| Mockito.verifyZeroInteractions(completionListener); |
| } |
| |
| @Test |
| public void testSeekDuringPlaybackUpdatesPosition() { |
| shadowMediaPlayer.setState(PREPARED); |
| |
| // This time offset is just to make sure that it doesn't work by |
| // accident because the offsets are calculated relative to 0. |
| scheduler.advanceBy(100); |
| |
| mediaPlayer.start(); |
| |
| scheduler.advanceBy(400); |
| assertThat(mediaPlayer.getCurrentPosition()).isEqualTo(400); |
| |
| mediaPlayer.seekTo(600); |
| scheduler.advanceBy(0); |
| assertThat(mediaPlayer.getCurrentPosition()).isEqualTo(600); |
| |
| scheduler.advanceBy(300); |
| assertThat(mediaPlayer.getCurrentPosition()).isEqualTo(900); |
| |
| mediaPlayer.seekTo(100); |
| scheduler.advanceBy(0); |
| assertThat(mediaPlayer.getCurrentPosition()).isEqualTo(100); |
| |
| scheduler.advanceBy(900); |
| assertThat(mediaPlayer.getCurrentPosition()).isEqualTo(1000); |
| assertThat(shadowMediaPlayer.getState()).isEqualTo(PLAYBACK_COMPLETED); |
| |
| scheduler.advanceBy(100); |
| assertThat(mediaPlayer.getCurrentPosition()).isEqualTo(1000); |
| } |
| |
| @Test |
| public void testPendingEventsRemovedOnError() { |
| Mockito.when(errorListener.onError(mediaPlayer, 2, 3)).thenReturn(true); |
| shadowMediaPlayer.setState(PREPARED); |
| mediaPlayer.start(); |
| scheduler.advanceBy(200); |
| |
| // We should have a pending completion callback. |
| assertThat(scheduler.enqueuedTaskCount()).isEqualTo(1); |
| |
| shadowMediaPlayer.invokeErrorListener(2, 3); |
| assertThat(scheduler.advanceToLastPostedRunnable()).isFalse(); |
| Mockito.verifyZeroInteractions(completionListener); |
| } |
| |
| @Test |
| public void testAttachAuxEffectStates() { |
| testStates(new MethodSpec("attachAuxEffect", 37), EnumSet.of(IDLE, ERROR), |
| onErrorTester, null); |
| } |
| |
| private static final EnumSet<State> emptyStateSet = EnumSet |
| .noneOf(State.class); |
| |
| @Test |
| public void testGetAudioSessionIdStates() { |
| testStates("getAudioSessionId", emptyStateSet, onErrorTester, null); |
| } |
| |
| @Test |
| public void testGetCurrentPositionStates() { |
| testStates("getCurrentPosition", EnumSet.of(IDLE, ERROR), onErrorTester, |
| null); |
| } |
| |
| @Test |
| public void testGetDurationStates() { |
| testStates("getDuration", EnumSet.of(IDLE, INITIALIZED, ERROR), |
| onErrorTester, null); |
| } |
| |
| @Test |
| public void testGetVideoHeightAndWidthStates() { |
| testStates("getVideoHeight", EnumSet.of(IDLE, ERROR), logTester, null); |
| testStates("getVideoWidth", EnumSet.of(IDLE, ERROR), logTester, null); |
| } |
| |
| @Test |
| public void testIsLoopingStates() { |
| // isLooping is quite unique as it throws ISE when in END state, |
| // even though every other state is legal. |
| testStates("isLooping", EnumSet.of(END), iseTester, null); |
| } |
| |
| @Test |
| public void testIsPlayingStates() { |
| testStates("isPlaying", EnumSet.of(ERROR), onErrorTester, null); |
| } |
| |
| @Test |
| public void testPauseStates() { |
| testStates("pause", |
| EnumSet.of(IDLE, INITIALIZED, PREPARED, STOPPED, ERROR), onErrorTester, |
| PAUSED); |
| } |
| |
| @Test |
| public void testPrepareStates() { |
| testStates("prepare", |
| EnumSet.of(IDLE, PREPARED, STARTED, PAUSED, PLAYBACK_COMPLETED, ERROR), |
| PREPARED); |
| } |
| |
| @Test |
| public void testPrepareAsyncStates() { |
| testStates("prepareAsync", |
| EnumSet.of(IDLE, PREPARED, STARTED, PAUSED, PLAYBACK_COMPLETED, ERROR), |
| PREPARING); |
| } |
| |
| @Test |
| public void testReleaseStates() { |
| testStates("release", emptyStateSet, END); |
| } |
| |
| @Test |
| public void testResetStates() { |
| testStates("reset", EnumSet.of(END), IDLE); |
| } |
| |
| @Test |
| public void testSeekToStates() { |
| testStates(new MethodSpec("seekTo", 38), |
| EnumSet.of(IDLE, INITIALIZED, STOPPED, ERROR), onErrorTester, null); |
| } |
| |
| @Test |
| public void testSetAudioSessionIdStates() { |
| testStates(new MethodSpec("setAudioSessionId", 40), EnumSet.of(INITIALIZED, |
| PREPARED, STARTED, PAUSED, STOPPED, PLAYBACK_COMPLETED, ERROR), |
| onErrorTester, null); |
| } |
| |
| // NOTE: This test diverges from the spec in the MediaPlayer |
| // doc, which says that setAudioStreamType() is valid to call |
| // from any state other than ERROR. It mentions that |
| // unless you call it before prepare it won't be effective. |
| // However, by inspection I found that it actually calls onError |
| // and moves into the ERROR state unless invoked from IDLE state, |
| // so that is what I have emulated. |
| @Test |
| public void testSetAudioStreamTypeStates() { |
| testStates(new MethodSpec("setAudioStreamType", AudioManager.STREAM_MUSIC), |
| EnumSet.of(PREPARED, STARTED, PAUSED, PLAYBACK_COMPLETED, ERROR), |
| onErrorTester, null); |
| } |
| |
| @Test |
| public void testSetLoopingStates() { |
| testStates(new MethodSpec("setLooping", true), EnumSet.of(ERROR), |
| onErrorTester, null); |
| } |
| |
| @Test |
| public void testSetVolumeStates() { |
| testStates(new MethodSpec("setVolume", new Class<?>[] { float.class, |
| float.class }, new Object[] { 1.0f, 1.0f }), EnumSet.of(ERROR), |
| onErrorTester, null); |
| } |
| |
| @Test |
| public void testSetDataSourceStates() { |
| final EnumSet<State> invalidStates = EnumSet.of(INITIALIZED, PREPARED, |
| STARTED, PAUSED, PLAYBACK_COMPLETED, STOPPED, ERROR); |
| final MethodSpec[] methodSpecs = { |
| new MethodSpec("setDataSource", "dummyFile"), |
| new MethodSpec("setDataSource", new Class<?>[] { Context.class, |
| Uri.class }, new Object[] { null, null }), |
| new MethodSpec("setDataSource", new Class<?>[] { Context.class, |
| Uri.class, Map.class }, new Object[] { null, null, null }), |
| new MethodSpec("setDataSource", FileDescriptor.class), |
| new MethodSpec("setDataSource", new Class<?>[] { FileDescriptor.class, |
| long.class, long.class }, new Object[] { null, 1L, 10L }) }; |
| |
| for (MethodSpec methodSpec : methodSpecs) { |
| testStates(methodSpec, invalidStates, iseTester, INITIALIZED); |
| } |
| } |
| |
| @Test |
| public void testStartStates() { |
| testStates("start", |
| EnumSet.of(IDLE, INITIALIZED, PREPARING, STOPPED, ERROR), |
| onErrorTester, STARTED); |
| } |
| |
| @Test |
| public void testStopStates() { |
| testStates("stop", EnumSet.of(IDLE, INITIALIZED, ERROR), onErrorTester, |
| STOPPED); |
| } |
| |
| @Test |
| public void testCurrentPosition() { |
| int[] positions = { 0, 1, 2, 1024 }; |
| for (int position : positions) { |
| shadowMediaPlayer.setCurrentPosition(position); |
| assertThat(mediaPlayer.getCurrentPosition()).isEqualTo(position); |
| } |
| } |
| |
| @Test |
| public void testInitialAudioSessionIdIsNotZero() { |
| assertThat(mediaPlayer.getAudioSessionId()).as("initial audioSessionId") |
| .isNotEqualTo(0); |
| } |
| |
| private Tester onErrorTester = new OnErrorTester(-38, 0); |
| private Tester iseTester = new ExceptionTester(IllegalStateException.class); |
| private Tester logTester = new LogTester(null); |
| private Tester assertTester = new ExceptionTester(AssertionError.class); |
| |
| private void testStates(String methodName, EnumSet<State> invalidStates, |
| State nextState) { |
| testStates(new MethodSpec(methodName), invalidStates, iseTester, nextState); |
| } |
| |
| public class MethodSpec { |
| public Method method; |
| // public String method; |
| public Class<?>[] argTypes; |
| public Object[] args; |
| |
| public MethodSpec(String method) { |
| this(method, (Class<?>[]) null, (Object[]) null); |
| } |
| |
| public MethodSpec(String method, Class<?>[] argTypes, Object[] args) { |
| try { |
| this.method = MediaPlayer.class.getDeclaredMethod(method, argTypes); |
| this.args = args; |
| } catch (NoSuchMethodException e) { |
| throw new AssertionError("Method lookup failed: " + method, e); |
| } |
| } |
| |
| public MethodSpec(String method, int arg) { |
| this(method, new Class<?>[] { int.class }, new Object[] { arg }); |
| } |
| |
| public MethodSpec(String method, boolean arg) { |
| this(method, new Class<?>[] { boolean.class }, new Object[] { arg }); |
| } |
| |
| public MethodSpec(String method, Class<?> c) { |
| this(method, new Class<?>[] { c }, new Object[] { null }); |
| } |
| |
| public MethodSpec(String method, Object o) { |
| this(method, new Class<?>[] { o.getClass() }, new Object[] { o }); |
| } |
| |
| public <T> MethodSpec(String method, T o, Class<T> c) { |
| this(method, new Class<?>[] { c }, new Object[] { o }); |
| } |
| |
| public void invoke() throws InvocationTargetException { |
| try { |
| method.invoke(mediaPlayer, args); |
| } catch (IllegalAccessException e) { |
| throw new AssertionError(e); |
| } |
| } |
| |
| public String toString() { |
| return method.toString(); |
| } |
| } |
| |
| private void testStates(String method, EnumSet<State> invalidStates, |
| Tester tester, State next) { |
| testStates(new MethodSpec(method), invalidStates, tester, next); |
| } |
| |
| private void testStates(MethodSpec method, EnumSet<State> invalidStates, |
| Tester tester, State next) { |
| final EnumSet<State> invalid = EnumSet.copyOf(invalidStates); |
| |
| // The documentation specifies that the behavior of calling any |
| // function while in the PREPARING state is undefined. I tried |
| // to play it safe but reasonable, by looking at whether the PREPARED or |
| // INITIALIZED are allowed (ie, the two states that PREPARING |
| // sites between). Only if both these states are allowed is |
| // PREPARING allowed too, if either PREPARED or INITALIZED is |
| // disallowed then so is PREPARING. |
| if (invalid.contains(PREPARED) || invalid.contains(INITIALIZED)) { |
| invalid.add(PREPARING); |
| } |
| shadowMediaPlayer.setInvalidStateBehavior(InvalidStateBehavior.SILENT); |
| for (State state : State.values()) { |
| shadowMediaPlayer.setState(state); |
| testMethodSuccess(method, next); |
| } |
| |
| shadowMediaPlayer.setInvalidStateBehavior(InvalidStateBehavior.EMULATE); |
| for (State state : invalid) { |
| shadowMediaPlayer.setState(state); |
| tester.test(method); |
| } |
| for (State state : EnumSet.complementOf(invalid)) { |
| if (state == END) { |
| continue; |
| } |
| shadowMediaPlayer.setState(state); |
| testMethodSuccess(method, next); |
| } |
| |
| // END state: by inspection we determined that if a method |
| // doesn't raise any kind of error in any other state then neither |
| // will it raise one in the END state; however if it raises errors |
| // in other states of any kind then it will throw |
| // IllegalArgumentException when in END. |
| shadowMediaPlayer.setState(END); |
| if (invalid.isEmpty()) { |
| testMethodSuccess(method, END); |
| } else { |
| iseTester.test(method); |
| } |
| |
| shadowMediaPlayer.setInvalidStateBehavior(InvalidStateBehavior.ASSERT); |
| for (State state : invalid) { |
| shadowMediaPlayer.setState(state); |
| assertTester.test(method); |
| } |
| for (State state : EnumSet.complementOf(invalid)) { |
| if (state == END) { |
| continue; |
| } |
| shadowMediaPlayer.setState(state); |
| testMethodSuccess(method, next); |
| } |
| shadowMediaPlayer.setState(END); |
| if (invalid.isEmpty()) { |
| testMethodSuccess(method, END); |
| } else { |
| assertTester.test(method); |
| } |
| } |
| |
| private interface Tester { |
| public void test(MethodSpec method); |
| } |
| |
| private class OnErrorTester implements Tester { |
| private int what; |
| private int extra; |
| |
| public OnErrorTester(int what, int extra) { |
| this.what = what; |
| this.extra = extra; |
| } |
| |
| public void test(MethodSpec method) { |
| final State state = shadowMediaPlayer.getState(); |
| final boolean wasPaused = scheduler.isPaused(); |
| scheduler.pause(); |
| try { |
| method.invoke(); |
| } catch (InvocationTargetException e) { |
| Assertions.fail("Expected <" + method |
| + "> to call onError rather than throw <" + e.getTargetException() |
| + "> when called from <" + state + ">", e); |
| } |
| Mockito.verifyZeroInteractions(errorListener); |
| final State finalState = shadowMediaPlayer.getState(); |
| assertThat(finalState) |
| .overridingErrorMessage( |
| "Expected state to change to ERROR when <%s> called from state <%s>, was <%s>", |
| method, state, finalState).isSameAs(ERROR); |
| scheduler.unPause(); |
| Mockito.verify(errorListener).onError(mediaPlayer, what, extra); |
| Mockito.reset(errorListener); |
| if (wasPaused) { |
| scheduler.pause(); |
| } |
| } |
| } |
| |
| private class ExceptionTester implements Tester { |
| private Class<? extends Throwable> eClass; |
| |
| public ExceptionTester(Class<? extends Throwable> eClass) { |
| this.eClass = eClass; |
| } |
| |
| public void test(MethodSpec method) { |
| final State state = shadowMediaPlayer.getState(); |
| boolean success = false; |
| try { |
| method.invoke(); |
| success = true; |
| } catch (InvocationTargetException e) { |
| Throwable cause = e.getTargetException(); |
| assertThat(cause) |
| .overridingErrorMessage( |
| "Unexpected exception <%s> thrown when <%s> called from state <%s>, expecting <%s>", |
| cause, method, state, eClass).isInstanceOf(eClass); |
| final State finalState = shadowMediaPlayer.getState(); |
| assertThat(finalState) |
| .overridingErrorMessage( |
| "Expected player to remain in <%s> state when <%s> called, was <%s>", |
| state, method, finalState).isSameAs(state); |
| } |
| assertThat(success) |
| .overridingErrorMessage( |
| "No exception thrown, expected <%s> when <%s> called from state <%s>", |
| eClass, method, state).isFalse(); |
| } |
| } |
| |
| private class LogTester implements Tester { |
| private State next; |
| |
| public LogTester(State next) { |
| this.next = next; |
| } |
| |
| public void test(MethodSpec method) { |
| testMethodSuccess(method, next); |
| } |
| } |
| |
| private void testMethodSuccess(MethodSpec method, State next) { |
| final State state = shadowMediaPlayer.getState(); |
| try { |
| method.invoke(); |
| final State finalState = shadowMediaPlayer.getState(); |
| if (next == null) { |
| assertThat(finalState) |
| .overridingErrorMessage( |
| "Expected state <%s> to remain unchanged when <%s> called, was <%s>", |
| state, method, finalState).isEqualTo(state); |
| } else { |
| assertThat(finalState).overridingErrorMessage( |
| "Expected <%s> to change state from <%s> to <%s>, was <%s>", |
| method, state, next, finalState).isEqualTo(next); |
| } |
| } catch (InvocationTargetException e) { |
| Throwable cause = e.getTargetException(); |
| Assertions |
| .fail("<" + method + "> should not throw Exception when in state <" |
| + state + ">", cause); |
| } |
| } |
| |
| private static final State[] seekableStates = { PREPARED, PAUSED, |
| PLAYBACK_COMPLETED, STARTED }; |
| |
| // It is not 100% clear from the docs if seeking to < 0 should |
| // invoke an error. I have assumed from the documentation |
| // which says "Successful invoke of this method in a valid |
| // state does not change the state" that it doesn't invoke an |
| // error. Rounding the seek up to 0 seems to be the sensible |
| // alternative behavior. |
| @Test |
| public void testSeekBeforeStart() { |
| shadowMediaPlayer.setSeekDelay(-1); |
| for (State state : seekableStates) { |
| shadowMediaPlayer.setState(state); |
| shadowMediaPlayer.setCurrentPosition(500); |
| |
| mediaPlayer.seekTo(-1); |
| shadowMediaPlayer.invokeSeekCompleteListener(); |
| |
| assertThat(mediaPlayer.getCurrentPosition()).as( |
| "Current postion while " + state).isEqualTo(0); |
| assertThat(shadowMediaPlayer.getState()).as("Final state " + state) |
| .isEqualTo(state); |
| } |
| } |
| |
| // Similar comments apply to this test as to |
| // testSeekBeforeStart(). |
| @Test |
| public void testSeekPastEnd() { |
| shadowMediaPlayer.setSeekDelay(-1); |
| for (State state : seekableStates) { |
| shadowMediaPlayer.setState(state); |
| shadowMediaPlayer.setCurrentPosition(500); |
| mediaPlayer.seekTo(1001); |
| shadowMediaPlayer.invokeSeekCompleteListener(); |
| |
| assertThat(mediaPlayer.getCurrentPosition()).as( |
| "Current postion while " + state).isEqualTo(1000); |
| assertThat(shadowMediaPlayer.getState()).as("Final state " + state) |
| .isEqualTo(state); |
| } |
| } |
| |
| @Test |
| public void testCompletionListener() { |
| shadowMediaPlayer.invokeCompletionListener(); |
| |
| Mockito.verify(completionListener).onCompletion(mediaPlayer); |
| } |
| |
| @Test |
| public void testCompletionWithoutListenerDoesNotThrowException() { |
| mediaPlayer.setOnCompletionListener(null); |
| shadowMediaPlayer.invokeCompletionListener(); |
| |
| assertThat(shadowMediaPlayer.getState()).isEqualTo(PLAYBACK_COMPLETED); |
| Mockito.verifyZeroInteractions(completionListener); |
| } |
| |
| @Test |
| public void testSeekListener() { |
| shadowMediaPlayer.invokeSeekCompleteListener(); |
| |
| Mockito.verify(seekListener).onSeekComplete(mediaPlayer); |
| } |
| |
| @Test |
| public void testSeekWithoutListenerDoesNotThrowException() { |
| mediaPlayer.setOnSeekCompleteListener(null); |
| shadowMediaPlayer.invokeSeekCompleteListener(); |
| |
| Mockito.verifyZeroInteractions(seekListener); |
| } |
| |
| @Test |
| public void testSeekDuringPlaybackDelayedCallback() { |
| shadowMediaPlayer.setState(PREPARED); |
| shadowMediaPlayer.setSeekDelay(100); |
| |
| assertThat(shadowMediaPlayer.getSeekDelay()).isEqualTo(100); |
| |
| mediaPlayer.start(); |
| scheduler.advanceBy(200); |
| assertThat(mediaPlayer.getCurrentPosition()).isEqualTo(200); |
| mediaPlayer.seekTo(450); |
| |
| assertThat(mediaPlayer.getCurrentPosition()).isEqualTo(200); |
| |
| scheduler.advanceBy(99); |
| |
| assertThat(mediaPlayer.getCurrentPosition()).isEqualTo(200); |
| Mockito.verifyZeroInteractions(seekListener); |
| |
| scheduler.advanceBy(1); |
| |
| assertThat(mediaPlayer.getCurrentPosition()).isEqualTo(450); |
| Mockito.verify(seekListener).onSeekComplete(mediaPlayer); |
| |
| assertThat(scheduler.advanceToLastPostedRunnable()).isTrue(); |
| Mockito.verifyNoMoreInteractions(seekListener); |
| } |
| |
| @Test |
| public void testSeekWhilePausedDelayedCallback() { |
| shadowMediaPlayer.setState(PAUSED); |
| shadowMediaPlayer.setSeekDelay(100); |
| |
| scheduler.advanceBy(200); |
| mediaPlayer.seekTo(450); |
| scheduler.advanceBy(99); |
| |
| assertThat(mediaPlayer.getCurrentPosition()).isEqualTo(0); |
| Mockito.verifyZeroInteractions(seekListener); |
| |
| scheduler.advanceBy(1); |
| |
| assertThat(mediaPlayer.getCurrentPosition()).isEqualTo(450); |
| Mockito.verify(seekListener).onSeekComplete(mediaPlayer); |
| // Check that no completion callback or alternative |
| // seek callbacks have been scheduled. |
| assertThat(scheduler.advanceToLastPostedRunnable()).isFalse(); |
| } |
| |
| @Test |
| public void testSeekWhileSeekingWhilePaused() { |
| shadowMediaPlayer.setState(PAUSED); |
| shadowMediaPlayer.setSeekDelay(100); |
| |
| scheduler.advanceBy(200); |
| mediaPlayer.seekTo(450); |
| scheduler.advanceBy(50); |
| mediaPlayer.seekTo(600); |
| scheduler.advanceBy(99); |
| |
| assertThat(mediaPlayer.getCurrentPosition()).isEqualTo(0); |
| Mockito.verifyZeroInteractions(seekListener); |
| |
| scheduler.advanceBy(1); |
| |
| assertThat(mediaPlayer.getCurrentPosition()).isEqualTo(600); |
| Mockito.verify(seekListener).onSeekComplete(mediaPlayer); |
| // Check that no completion callback or alternative |
| // seek callbacks have been scheduled. |
| assertThat(scheduler.advanceToLastPostedRunnable()).isFalse(); |
| } |
| |
| @Test |
| public void testSeekWhileSeekingWhilePlaying() { |
| shadowMediaPlayer.setState(PREPARED); |
| shadowMediaPlayer.setSeekDelay(100); |
| |
| mediaPlayer.start(); |
| scheduler.advanceBy(200); |
| mediaPlayer.seekTo(450); |
| scheduler.advanceBy(50); |
| mediaPlayer.seekTo(600); |
| scheduler.advanceBy(99); |
| |
| // Not sure of the correct behavior to emulate here, as the MediaPlayer |
| // documentation is not detailed enough. There are three possibilities: |
| // 1. Playback is paused for the entire time that a seek is in progress. |
| // 2. Playback continues normally until the seek is complete. |
| // 3. Somewhere between these two extremes - playback continues for |
| // a while and then pauses until the seek is complete. |
| // I have decided to emulate the first. I don't think that |
| // implementations should depend on any of these particular behaviors |
| // and consider the behavior indeterminate. |
| assertThat(mediaPlayer.getCurrentPosition()).isEqualTo(200); |
| Mockito.verifyZeroInteractions(seekListener); |
| |
| scheduler.advanceBy(1); |
| |
| assertThat(mediaPlayer.getCurrentPosition()).isEqualTo(600); |
| Mockito.verify(seekListener).onSeekComplete(mediaPlayer); |
| // Check that the completion callback is scheduled properly |
| // but no alternative seek callbacks. |
| assertThat(scheduler.advanceToLastPostedRunnable()).isTrue(); |
| Mockito.verify(completionListener).onCompletion(mediaPlayer); |
| Mockito.verifyNoMoreInteractions(seekListener); |
| assertThat(scheduler.getCurrentTime()).isEqualTo(750); |
| assertThat(mediaPlayer.getCurrentPosition()).isEqualTo(1000); |
| assertThat(shadowMediaPlayer.getState()).isEqualTo(PLAYBACK_COMPLETED); |
| } |
| |
| @Test |
| public void testSimulatenousEventsAllRun() { |
| // Simultaneous events should all run even if |
| // one of them stops playback. |
| Runnable r1 = new Runnable() { |
| public void run() { |
| shadowMediaPlayer.doStop(); |
| } |
| }; |
| Runnable r2 = Mockito.mock(Runnable.class); |
| |
| MediaInfo info = shadowMediaPlayer.getDefaultMediaInfo(); |
| |
| info.scheduleEventAtOffset(100, r1); |
| info.scheduleEventAtOffset(100, r2); |
| |
| shadowMediaPlayer.doStart(); |
| scheduler.advanceBy(100); |
| Mockito.verify(r2).run(); |
| } |
| |
| // FIXME: These next two tests fail due to a bug in the ShadowHandler |
| // implementation - the callbacks are |
| // removed from the Handler but not from the Scheduler, so |
| // they run anyway. Need to fix and re-instate these tests. |
| // @Test |
| // public void testResetCancelsCallbacks() { |
| // shadowMediaPlayer.setState(STARTED); |
| // mediaPlayer.seekTo(100); |
| // Runnable r = Mockito.mock(Runnable.class); |
| // shadowMediaPlayer.getHandler().postDelayed(r, 200); |
| // mediaPlayer.reset(); |
| // |
| // assertThat(scheduler.size()).isEqualTo(0); |
| // } |
| // |
| // @Test |
| // public void testReleaseCancelsSeekCallback() { |
| // shadowMediaPlayer.setState(STARTED); |
| // mediaPlayer.seekTo(100); |
| // Runnable r = Mockito.mock(Runnable.class); |
| // shadowMediaPlayer.getHandler().postDelayed(r, 200); |
| // mediaPlayer.release(); |
| // |
| // assertThat(scheduler.size()).isEqualTo(0); |
| // } |
| |
| @Test |
| public void testSeekManualCallback() { |
| // Need to put the player into a state where seeking is allowed |
| shadowMediaPlayer.setState(STARTED); |
| // seekDelay of -1 signifies that OnSeekComplete won't be |
| // invoked automatically by the shadow player itself. |
| shadowMediaPlayer.setSeekDelay(-1); |
| |
| assertThat(shadowMediaPlayer.getPendingSeek()).as("pendingSeek before") |
| .isEqualTo(-1); |
| int[] positions = { 0, 5, 2, 999 }; |
| int prevPos = 0; |
| for (int position : positions) { |
| mediaPlayer.seekTo(position); |
| |
| assertThat(shadowMediaPlayer.getPendingSeek()).as("pendingSeek") |
| .isEqualTo(position); |
| assertThat(mediaPlayer.getCurrentPosition()).as("pendingSeekCurrentPos") |
| .isEqualTo(prevPos); |
| |
| shadowMediaPlayer.invokeSeekCompleteListener(); |
| |
| assertThat(shadowMediaPlayer.getPendingSeek()).isEqualTo(-1); |
| assertThat(mediaPlayer.getCurrentPosition()).isEqualTo(position); |
| prevPos = position; |
| } |
| } |
| |
| @Test |
| public void testPreparedListenerCalled() { |
| shadowMediaPlayer.invokePreparedListener(); |
| assertThat(shadowMediaPlayer.getState()).isEqualTo(PREPARED); |
| Mockito.verify(preparedListener).onPrepared(mediaPlayer); |
| } |
| |
| @Test |
| public void testPreparedWithoutListenerDoesNotThrowException() { |
| mediaPlayer.setOnPreparedListener(null); |
| shadowMediaPlayer.invokePreparedListener(); |
| |
| assertThat(shadowMediaPlayer.getState()).isEqualTo(PREPARED); |
| Mockito.verifyZeroInteractions(preparedListener); |
| } |
| |
| @Test |
| public void testInfoListenerCalled() { |
| shadowMediaPlayer.invokeInfoListener(21, 32); |
| Mockito.verify(infoListener).onInfo(mediaPlayer, 21, 32); |
| } |
| |
| @Test |
| public void testInfoWithoutListenerDoesNotThrowException() { |
| mediaPlayer.setOnInfoListener(null); |
| shadowMediaPlayer.invokeInfoListener(3, 44); |
| |
| Mockito.verifyZeroInteractions(infoListener); |
| } |
| |
| @Test |
| public void testErrorListenerCalledNoOnCompleteCalledWhenReturnTrue() { |
| Mockito.when(errorListener.onError(mediaPlayer, 112, 221)).thenReturn(true); |
| |
| shadowMediaPlayer.invokeErrorListener(112, 221); |
| |
| assertThat(shadowMediaPlayer.getState()).isEqualTo(ERROR); |
| Mockito.verify(errorListener).onError(mediaPlayer, 112, 221); |
| Mockito.verifyZeroInteractions(completionListener); |
| } |
| |
| @Test |
| public void testErrorListenerCalledOnCompleteCalledWhenReturnFalse() { |
| Mockito.when(errorListener.onError(mediaPlayer, 0, 0)).thenReturn(false); |
| |
| shadowMediaPlayer.invokeErrorListener(321, 11); |
| |
| Mockito.verify(errorListener).onError(mediaPlayer, 321, 11); |
| Mockito.verify(completionListener).onCompletion(mediaPlayer); |
| } |
| |
| @Test |
| public void testErrorCausesOnCompleteCalledWhenNoErrorListener() { |
| mediaPlayer.setOnErrorListener(null); |
| |
| shadowMediaPlayer.invokeErrorListener(321, 21); |
| |
| Mockito.verifyZeroInteractions(errorListener); |
| Mockito.verify(completionListener).onCompletion(mediaPlayer); |
| } |
| |
| @Test |
| public void testReleaseStopsScheduler() { |
| shadowMediaPlayer.doStart(); |
| mediaPlayer.release(); |
| assertThat(scheduler.size()).isEqualTo(0); |
| } |
| |
| @Test |
| public void testResetStopsScheduler() { |
| shadowMediaPlayer.doStart(); |
| mediaPlayer.reset(); |
| assertThat(scheduler.size()).isEqualTo(0); |
| } |
| |
| @Test |
| public void testDoStartStop() { |
| assertThat(shadowMediaPlayer.isReallyPlaying()).isFalse(); |
| scheduler.advanceBy(100); |
| shadowMediaPlayer.doStart(); |
| assertThat(shadowMediaPlayer.isReallyPlaying()).isTrue(); |
| assertThat(shadowMediaPlayer.getCurrentPositionRaw()).isEqualTo(0); |
| assertThat(shadowMediaPlayer.getState()).isSameAs(IDLE); |
| |
| scheduler.advanceBy(100); |
| assertThat(shadowMediaPlayer.getCurrentPositionRaw()).isEqualTo(100); |
| |
| shadowMediaPlayer.doStop(); |
| assertThat(shadowMediaPlayer.isReallyPlaying()).isFalse(); |
| assertThat(shadowMediaPlayer.getCurrentPositionRaw()).isEqualTo(100); |
| assertThat(shadowMediaPlayer.getState()).isSameAs(IDLE); |
| |
| scheduler.advanceBy(50); |
| assertThat(shadowMediaPlayer.getCurrentPositionRaw()).isEqualTo(100); |
| } |
| |
| @Test |
| public void testScheduleErrorAtOffsetWhileNotPlaying() { |
| shadowMediaPlayer.getDefaultMediaInfo().scheduleErrorAtOffset(500, 1, 3); |
| shadowMediaPlayer.setState(PREPARED); |
| mediaPlayer.start(); |
| |
| scheduler.advanceBy(499); |
| Mockito.verifyZeroInteractions(errorListener); |
| |
| scheduler.advanceBy(1); |
| Mockito.verify(errorListener).onError(mediaPlayer, 1, 3); |
| assertThat(shadowMediaPlayer.getState()).isSameAs(ERROR); |
| assertThat(scheduler.advanceToLastPostedRunnable()).isFalse(); |
| assertThat(shadowMediaPlayer.getCurrentPositionRaw()).isEqualTo(500); |
| } |
| |
| @Test |
| public void testScheduleErrorAtOffsetWhilePlaying() { |
| shadowMediaPlayer.setState(PREPARED); |
| mediaPlayer.start(); |
| |
| scheduler.advanceBy(200); |
| |
| shadowMediaPlayer.getDefaultMediaInfo().scheduleErrorAtOffset(500, 1, 3); |
| |
| scheduler.advanceBy(299); |
| Mockito.verifyZeroInteractions(errorListener); |
| |
| scheduler.advanceBy(1); |
| Mockito.verify(errorListener).onError(mediaPlayer, 1, 3); |
| Mockito.verify(completionListener).onCompletion(mediaPlayer); |
| assertThat(scheduler.advanceToLastPostedRunnable()).isFalse(); |
| assertThat(shadowMediaPlayer.getCurrentPositionRaw()).isEqualTo(500); |
| } |
| |
| @Test |
| public void testScheduleErrorAtOffsetInPast() { |
| shadowMediaPlayer.setCurrentPosition(400); |
| shadowMediaPlayer.setState(PAUSED); |
| shadowMediaPlayer.getDefaultMediaInfo().scheduleErrorAtOffset(200, 1, 2); |
| mediaPlayer.start(); |
| scheduler.unPause(); |
| Mockito.verifyZeroInteractions(errorListener); |
| } |
| |
| @Test |
| public void testScheduleInfoAtOffsetWhilePlaying() { |
| shadowMediaPlayer.setState(PREPARED); |
| mediaPlayer.start(); |
| |
| scheduler.advanceBy(200); |
| |
| shadowMediaPlayer.getDefaultMediaInfo().scheduleInfoAtOffset(500, 1, 3); |
| |
| scheduler.advanceBy(299); |
| Mockito.verifyZeroInteractions(infoListener); |
| |
| scheduler.advanceBy(1); |
| Mockito.verify(infoListener).onInfo(mediaPlayer, 1, 3); |
| Mockito.verifyZeroInteractions(completionListener); |
| } |
| |
| @Test |
| public void testScheduleBufferUnderrunAtOffset() { |
| shadowMediaPlayer.getDefaultMediaInfo().scheduleBufferUnderrunAtOffset(100, |
| 50); |
| shadowMediaPlayer.setState(PREPARED); |
| mediaPlayer.start(); |
| |
| scheduler.advanceBy(99); |
| |
| Mockito.verifyZeroInteractions(infoListener); |
| |
| scheduler.advanceBy(1); |
| Mockito.verify(infoListener).onInfo(mediaPlayer, |
| MediaPlayer.MEDIA_INFO_BUFFERING_START, 0); |
| assertThat(shadowMediaPlayer.getCurrentPositionRaw()).isEqualTo(100); |
| assertThat(shadowMediaPlayer.isReallyPlaying()).isFalse(); |
| |
| scheduler.advanceBy(49); |
| Mockito.verifyZeroInteractions(infoListener); |
| |
| scheduler.advanceBy(1); |
| assertThat(shadowMediaPlayer.getCurrentPositionRaw()).isEqualTo(100); |
| Mockito.verify(infoListener).onInfo(mediaPlayer, |
| MediaPlayer.MEDIA_INFO_BUFFERING_END, 0); |
| |
| scheduler.advanceBy(100); |
| assertThat(shadowMediaPlayer.getCurrentPositionRaw()).isEqualTo(200); |
| } |
| |
| @Test |
| public void testRemoveEventAtOffset() { |
| shadowMediaPlayer.setState(PREPARED); |
| mediaPlayer.start(); |
| |
| scheduler.advanceBy(200); |
| |
| Runnable r = shadowMediaPlayer.getDefaultMediaInfo().scheduleInfoAtOffset( |
| 500, 1, 3); |
| |
| scheduler.advanceBy(299); |
| shadowMediaPlayer.getDefaultMediaInfo().removeEventAtOffset(500, r); |
| scheduler.advanceToLastPostedRunnable(); |
| Mockito.verifyZeroInteractions(infoListener); |
| } |
| |
| @Test |
| public void testRemoveEvent() { |
| shadowMediaPlayer.setState(PREPARED); |
| mediaPlayer.start(); |
| |
| scheduler.advanceBy(200); |
| |
| Runnable r = shadowMediaPlayer.getDefaultMediaInfo().scheduleInfoAtOffset( |
| 500, 1, 3); |
| |
| scheduler.advanceBy(299); |
| shadowMediaPlayer.getDefaultMediaInfo().removeEvent(r); |
| scheduler.advanceToLastPostedRunnable(); |
| Mockito.verifyZeroInteractions(infoListener); |
| } |
| |
| @Test |
| public void testScheduleMultipleRunnables() { |
| shadowMediaPlayer.setState(PREPARED); |
| scheduler.advanceBy(25); |
| mediaPlayer.start(); |
| |
| scheduler.advanceBy(200); |
| MediaInfo mediaInfo = shadowMediaPlayer.getDefaultMediaInfo(); |
| assertThat(scheduler.size()).isEqualTo(1); |
| mediaInfo.scheduleInfoAtOffset(250, 2, 4); |
| assertThat(scheduler.size()).isEqualTo(1); |
| |
| Runnable r1 = Mockito.mock(Runnable.class); |
| |
| mediaInfo.scheduleEventAtOffset(400, r1); |
| |
| scheduler.advanceBy(49); |
| Mockito.verifyZeroInteractions(infoListener); |
| scheduler.advanceBy(1); |
| Mockito.verify(infoListener).onInfo(mediaPlayer, 2, 4); |
| scheduler.advanceBy(149); |
| mediaInfo.scheduleErrorAtOffset(675, 32, 22); |
| Mockito.verifyZeroInteractions(r1); |
| scheduler.advanceBy(1); |
| Mockito.verify(r1).run(); |
| |
| mediaPlayer.pause(); |
| assertThat(scheduler.size()).isEqualTo(0); |
| scheduler.advanceBy(324); |
| Runnable r2 = Mockito.mock(Runnable.class); |
| mediaInfo.scheduleEventAtOffset(680, r2); |
| mediaPlayer.start(); |
| scheduler.advanceBy(274); |
| Mockito.verifyZeroInteractions(errorListener); |
| |
| scheduler.advanceBy(1); |
| Mockito.verify(errorListener).onError(mediaPlayer, 32, 22); |
| assertThat(scheduler.size()).isEqualTo(0); |
| assertThat(shadowMediaPlayer.getCurrentPositionRaw()).isEqualTo(675); |
| assertThat(shadowMediaPlayer.getState()).isSameAs(ERROR); |
| Mockito.verifyZeroInteractions(r2); |
| } |
| |
| @Test |
| public void testSetSetDataSourceExceptionWithWrongExceptionTypeAsserts() { |
| boolean fail = false; |
| try { |
| shadowMediaPlayer.getDefaultMediaInfo().setSetDataSourceException( |
| new CloneNotSupportedException()); |
| fail = true; |
| } catch (AssertionError e) { |
| } |
| assertThat(fail) |
| .overridingErrorMessage( |
| "setSetDataSourceException() should assert with non-IOException,non-RuntimeException") |
| .isFalse(); |
| } |
| |
| @Test |
| public void testSetDataSourceThrowsAllowedException() { |
| Exception[] exceptions = { new IOException(), new SecurityException(), |
| new IllegalArgumentException() }; |
| for (Exception e : exceptions) { |
| shadowMediaPlayer.getDefaultMediaInfo().setSetDataSourceException(e); |
| |
| shadowMediaPlayer.setState(IDLE); |
| try { |
| mediaPlayer.setDataSource((FileDescriptor) null); |
| Assertions.fail("Expecting " + e + " to be thrown"); |
| } catch (Exception eThrown) { |
| assertThat(eThrown).isSameAs(e); |
| } |
| assertThat(shadowMediaPlayer.getState()).as( |
| "State shouldn't change when " + e + " thrown").isSameAs(IDLE); |
| |
| // Test all three flavors of setDataSource() |
| shadowMediaPlayer.setState(IDLE); |
| try { |
| mediaPlayer.setDataSource(null, null, null); |
| Assertions.fail("Expecting " + e + " to be thrown"); |
| } catch (Exception eThrown) { |
| assertThat(eThrown).isSameAs(e); |
| } |
| assertThat(shadowMediaPlayer.getState()).as( |
| "State shouldn't change when " + e + " thrown").isSameAs(IDLE); |
| |
| shadowMediaPlayer.setState(IDLE); |
| try { |
| mediaPlayer.setDataSource((String) null); |
| Assertions.fail("Expecting " + e + " to be thrown"); |
| } catch (Exception eThrown) { |
| assertThat(eThrown).isSameAs(e); |
| } |
| assertThat(shadowMediaPlayer.getState()).as( |
| "State shouldn't change when " + e + " thrown").isSameAs(IDLE); |
| } |
| } |
| |
| @Test |
| public void testSetDataSourceCustomeExceptionOveridesIllegalState() { |
| shadowMediaPlayer.setState(PREPARED); |
| shadowMediaPlayer.getDefaultMediaInfo().setSetDataSourceException( |
| new IOException()); |
| try { |
| mediaPlayer.setDataSource((String) null); |
| Assertions.fail("Expecting IOException to be thrown"); |
| } catch (IOException eThrown) { |
| } catch (Exception eThrown) { |
| Assertions.fail(eThrown + " was thrown, expecting IOException"); |
| } |
| } |
| |
| @Test |
| public void testMediaInfoOfSubsequentSDSCallsNotAffectedByEarlier() |
| throws IOException { |
| MediaInfo dummyInfo = shadowMediaPlayer.buildMediaInfo(3000, 2); |
| Map<String, MediaInfo> map = new HashMap<String, MediaInfo>(); |
| map.put("dummy", dummyInfo); |
| shadowMediaPlayer.setDataSourceMap(map); |
| mediaPlayer.setDataSource("dummy"); |
| |
| assertThat(shadowMediaPlayer.getCurrentMediaInfo()).isSameAs(dummyInfo); |
| |
| shadowMediaPlayer.setState(IDLE); |
| mediaPlayer.setDataSource("dummy2"); |
| |
| assertThat(shadowMediaPlayer.getCurrentMediaInfo()).isSameAs( |
| shadowMediaPlayer.getDefaultMediaInfo()); |
| } |
| |
| @Test |
| public void testGetSetLooping() { |
| assertThat(mediaPlayer.isLooping()).isFalse(); |
| mediaPlayer.setLooping(true); |
| assertThat(mediaPlayer.isLooping()).isTrue(); |
| mediaPlayer.setLooping(false); |
| assertThat(mediaPlayer.isLooping()).isFalse(); |
| } |
| |
| /** |
| * If the looping mode was being set to <code>true</code> |
| * {@link MediaPlayer#setLooping(boolean)}, the MediaPlayer object shall |
| * remain in the Started state. |
| */ |
| @Test |
| public void testSetLoopingCalledWhilePlaying() { |
| shadowMediaPlayer.setState(PREPARED); |
| mediaPlayer.start(); |
| scheduler.advanceBy(200); |
| |
| mediaPlayer.setLooping(true); |
| scheduler.advanceBy(1100); |
| |
| Mockito.verifyZeroInteractions(completionListener); |
| assertThat(mediaPlayer.getCurrentPosition()).isEqualTo(300); |
| |
| mediaPlayer.setLooping(false); |
| scheduler.advanceBy(699); |
| Mockito.verifyZeroInteractions(completionListener); |
| |
| scheduler.advanceBy(1); |
| Mockito.verify(completionListener).onCompletion(mediaPlayer); |
| } |
| |
| @Test |
| public void testSetLoopingCalledWhileStartable() { |
| final State[] startableStates = { PREPARED, PAUSED }; |
| for (State state : startableStates) { |
| shadowMediaPlayer.setCurrentPosition(500); |
| shadowMediaPlayer.setState(state); |
| |
| mediaPlayer.setLooping(true); |
| mediaPlayer.start(); |
| |
| scheduler.advanceBy(700); |
| Mockito.verifyZeroInteractions(completionListener); |
| assertThat(mediaPlayer.getCurrentPosition()).as(state.toString()) |
| .isEqualTo(200); |
| } |
| } |
| |
| /** |
| * While in the PlaybackCompleted state, calling start() can restart the |
| * playback from the beginning of the audio/video source. |
| */ |
| @Test |
| public void testStartAfterPlaybackCompleted() { |
| shadowMediaPlayer.setState(PLAYBACK_COMPLETED); |
| shadowMediaPlayer.setCurrentPosition(1000); |
| |
| mediaPlayer.start(); |
| assertThat(mediaPlayer.getCurrentPosition()).isEqualTo(0); |
| } |
| } |