/**
* The MIT License (MIT)
* Copyright (c) 2012 David Carver
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
* OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package us.nineworlds.serenity.ui.video.player;
import static org.fest.assertions.api.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import javax.inject.Singleton;
import org.fest.assertions.api.ANDROID;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowActivity;
import org.robolectric.shadows.ShadowApplication;
import org.robolectric.shadows.ShadowSurfaceView;
import us.nineworlds.serenity.R;
import us.nineworlds.serenity.SerenityRobolectricTestRunner;
import us.nineworlds.serenity.core.model.VideoContentInfo;
import us.nineworlds.serenity.core.model.impl.EpisodePosterInfo;
import us.nineworlds.serenity.injection.ForVideoQueue;
import us.nineworlds.serenity.injection.modules.AndroidModule;
import us.nineworlds.serenity.injection.modules.SerenityModule;
import us.nineworlds.serenity.test.InjectingTest;
import us.nineworlds.serenity.ui.video.player.SerenitySurfaceViewVideoActivity.VideoPlayerOnCompletionListener;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import dagger.Module;
import dagger.Provides;
@RunWith(SerenityRobolectricTestRunner.class)
@Config(emulateSdk = 18)
public class SerenitySurfaceViewVideoPlayerTest extends InjectingTest {
@Mock
KeyEvent keyEvent;
@Mock
MediaPlayer mockMediaPlayer;
@Mock
SurfaceHolder mockSurfaceHolder;
@Mock
ActionBar mockActionBar;
@Mock
Intent mockIntent;
@Mock
Bundle mockBundle;
@Mock
MediaControllerDataObject mockMediaControllerDataObject;
MediaController spyMediaController;
@Mock
LinkedList<VideoContentInfo> videoQueue;
@Mock
VideoContentInfo mockVideoContentInfo;
SerenitySurfaceViewVideoActivity activity;
@Override
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
super.setUp();
ShadowApplication shadowApplication = Robolectric
.shadowOf(Robolectric.application);
shadowApplication
.declareActionUnbindable("com.google.android.gms.analytics.service.START");
doReturn(true).when(videoQueue).isEmpty();
activity = Robolectric
.buildActivity(SerenitySurfaceViewVideoActivity.class).create()
.get();
doReturn(activity).when(mockMediaControllerDataObject).getContext();
spyMediaController = spy(new MediaController(
mockMediaControllerDataObject));
}
@After
public void tearDown() throws Exception {
videoQueue.clear();
}
@Test
public void onBackPressedIsCalledWhenKeyCodeBackIsReceived() {
SerenitySurfaceViewVideoActivity spyActivity = spy(activity);
doNothing().when(spyActivity).onBackPressed();
spyActivity.onKeyDown(KeyEvent.KEYCODE_BACK, keyEvent);
verify(spyActivity).onBackPressed();
}
@Test
public void onKeyDownReturnsTrueWhenKeyCodeBackIsReceived() {
SerenitySurfaceViewVideoActivity spyActivity = spy(activity);
doNothing().when(spyActivity).onBackPressed();
boolean result = spyActivity.onKeyDown(KeyEvent.KEYCODE_BACK, keyEvent);
assertThat(result).isTrue();
}
@Test
public void surfaceCreatedSetsMediaPlayerOnPreparedListener() {
doNothing().when(mockMediaPlayer).setOnPreparedListener(
any(VideoPlayerPrepareListener.class));
activity.surfaceCreated(mockSurfaceHolder);
verify(mockMediaPlayer).setOnPreparedListener(
any(VideoPlayerPrepareListener.class));
}
@Test
public void surfaceCreatedSetsMediaPlayerOnCompletionListener() {
doNothing().when(mockMediaPlayer).setOnCompletionListener(
any(VideoPlayerOnCompletionListener.class));
activity.surfaceCreated(mockSurfaceHolder);
verify(mockMediaPlayer).setOnCompletionListener(
any(VideoPlayerOnCompletionListener.class));
}
@Test
public void surfaceCreatedCallsMediaPlayerPrepareAsync() {
activity.surfaceCreated(mockSurfaceHolder);
verify(mockMediaPlayer).prepareAsync();
}
@Test
public void surfaceDestroyedReleasesMediaPlayerWhenNotInReleaseState() {
activity.setMediaplayerReleased(false);
activity.surfaceDestroyed(mockSurfaceHolder);
assertThat(activity.isMediaplayerReleased()).isTrue();
verify(mockMediaPlayer).release();
}
@Test
public void surfaceDestroyedDoesNotReleaseMediaPlayerWhenInReleaseState() {
activity.setMediaplayerReleased(true);
activity.surfaceDestroyed(mockSurfaceHolder);
assertThat(activity.isMediaplayerReleased()).isTrue();
verify(mockMediaPlayer, times(0)).release();
}
@Test
public void surfaceViewKeepScreenOnIsSetToTrueFromOnCreate() {
SurfaceView surfaceView = (SurfaceView) activity
.findViewById(R.id.surfaceView);
ANDROID.assertThat(surfaceView).isKeepingScreenOn();
}
@Test
public void surfaceViewHolderIsSetToAnInstanceOfSerenitySurfaceVideoPlayerActivity() {
SurfaceView surfaceView = (SurfaceView) activity
.findViewById(R.id.surfaceView);
ShadowSurfaceView shadowSurfaceView = (ShadowSurfaceView) Robolectric
.shadowOf(surfaceView);
Set callbacks = shadowSurfaceView.getFakeSurfaceHolder().getCallbacks();
assertThat(callbacks).isNotEmpty();
}
@Test
@Config(reportSdk = 13)
public void onCreateHidesCallsGetSupportActionBar() {
activity = Robolectric
.buildActivity(SerenitySurfaceViewVideoActivity.class).create()
.get();
ActionBar actionBar = activity.getSupportActionBar();
assertThat(actionBar.isShowing()).isFalse();
}
@Test
public void retrieveIntentExtrasWithIntentSetsAutoResumeFromBundleExtra() {
SerenitySurfaceViewVideoActivity spyActivity = spy(activity);
doReturn(mockBundle).when(mockIntent).getExtras();
doReturn(true).when(mockBundle).getBoolean("autoResume", false);
doReturn(mockIntent).when(spyActivity).getIntent();
doNothing().when(spyActivity).playBackFromVideoQueue();
doNothing().when(spyActivity).playbackFromIntent(any(Bundle.class));
spyActivity.retrieveIntentExtras();
verify(mockBundle).getBoolean("autoResume", false);
}
@Test
public void onKeyDownCallsVideoPlayKeyCodeHandler() {
VideoPlayerKeyCodeHandler spyKeyCodeHandler = spy(new VideoPlayerKeyCodeHandler(
null, null, 0, null, null, null, null));
activity.setVideoPlayerKeyCodeHandler(spyKeyCodeHandler);
doReturn(true).when(spyKeyCodeHandler).onKeyDown(anyInt(),
any(KeyEvent.class), anyBoolean());
boolean result = activity.onKeyDown(0, Mockito.mock(KeyEvent.class));
verify(spyKeyCodeHandler).onKeyDown(anyInt(), any(KeyEvent.class),
anyBoolean());
assertThat(result).isTrue();
}
@Test
public void onKeyDownReturnsFalseWhenKeyCodeNotHandled() {
VideoPlayerKeyCodeHandler spyKeyCodeHandler = spy(new VideoPlayerKeyCodeHandler(
null, null, 0, null, null, null, null));
activity.setVideoPlayerKeyCodeHandler(spyKeyCodeHandler);
doReturn(false).when(spyKeyCodeHandler).onKeyDown(anyInt(),
any(KeyEvent.class), anyBoolean());
boolean result = activity.onKeyDown(0, Mockito.mock(KeyEvent.class));
verify(spyKeyCodeHandler).onKeyDown(anyInt(), any(KeyEvent.class),
anyBoolean());
assertThat(result).isFalse();
}
@Test
public void onResumeCallsVisibileInBackground() {
SerenitySurfaceViewVideoActivity spyActivity = spy(activity);
doNothing().when(spyActivity).visibleInBackground();
spyActivity.onResume();
verify(spyActivity).visibleInBackground();
}
@Test
@Ignore
public void onResumeRequestsVisibileBehindOnLollipopDevicesOrHigher() {
SerenitySurfaceViewVideoActivity spyActivity = spy(activity);
doReturn(true).when(spyActivity).requestVisibleBehind(anyBoolean());
spyActivity.onResume();
verify(spyActivity).requestVisibleBehind(true);
}
@Test
public void onPauseCallsVisibileInBackground() {
SerenitySurfaceViewVideoActivity spyActivity = spy(activity);
doNothing().when(spyActivity).visibleInBackground();
spyActivity.onPause();
verify(spyActivity).visibleInBackground();
}
@Test
public void onBackPressedHidesMediaControllerIfItIsShowing() {
doNothing().when(spyMediaController).hide();
doReturn(true).when(spyMediaController).isShowing();
activity.setSerenityMediaController(spyMediaController);
activity.onBackPressed();
verify(spyMediaController).hide();
verify(spyMediaController).isShowing();
}
@Test
public void onBackPressedStopsPlayingMediaWhenPlayingAndInAValidState() {
SerenitySurfaceViewVideoActivity spyActivity = spy(activity);
doReturn(true).when(spyMediaController).isShowing();
doReturn(true).when(spyActivity).isMediaPlayerStateValid();
doReturn(true).when(mockMediaPlayer).isPlaying();
spyActivity.setSerenityMediaController(spyMediaController);
spyActivity.onBackPressed();
verify(mockMediaPlayer).stop();
verify(mockMediaPlayer).isPlaying();
verify(spyActivity).isMediaPlayerStateValid();
}
@Test
public void playbackFromVideoQueueDoesNothingWhenVideoQueueIsEmpty() {
activity.playBackFromVideoQueue();
verify(videoQueue, never()).poll();
}
@Test
public void playbackFromVideoQueueReturnsVideoContentInfoWhenPolled() {
SerenitySurfaceViewVideoActivity spyActivity = spy(activity);
doNothing().when(spyActivity).initMediaController(
any(MediaControllerDataObject.class));
doReturn(mockVideoContentInfo).when(videoQueue).poll();
doReturn(false).when(videoQueue).isEmpty();
spyActivity.playBackFromVideoQueue();
verify(videoQueue).poll();
verify(spyActivity).initMediaController(
any(MediaControllerDataObject.class));
}
@Test
public void playbackFromVideoQueueRetrievesParentPosterUrlWhenAnEpisodeIsFetchedFromTheQueue() {
SerenitySurfaceViewVideoActivity spyActivity = spy(activity);
EpisodePosterInfo videoContentInfo = Mockito
.mock(EpisodePosterInfo.class);
doNothing().when(spyActivity).initMediaController(
any(MediaControllerDataObject.class));
doReturn(videoContentInfo).when(videoQueue).poll();
doReturn("http://plexserver/some/entry.gif").when(videoContentInfo)
.getParentPosterURL();
doReturn(false).when(videoQueue).isEmpty();
spyActivity.playBackFromVideoQueue();
verify(videoQueue).poll();
verify(videoContentInfo, times(2)).getParentPosterURL();
verify(spyActivity).initMediaController(
any(MediaControllerDataObject.class));
}
@Test
public void setExitResultCodeSetsLastPlayBackPosition() {
activity.setPlaybackPos(100);
activity.setExitResultCodeFinished();
ShadowActivity shadowActivity = Robolectric.shadowOf(activity);
Intent resultIntent = shadowActivity.getResultIntent();
int position = resultIntent.getExtras().getInt("position");
assertThat(position).isEqualTo(100);
}
@Test
public void onTouchEventMotionActionDownHidesMediaControllerWhenShowing() {
MotionEvent mockMotionEvent = Mockito.mock(MotionEvent.class);
doReturn(MotionEvent.ACTION_DOWN).when(mockMotionEvent).getAction();
doReturn(true).when(spyMediaController).isShowing();
activity.setSerenityMediaController(spyMediaController);
activity.onTouchEvent(mockMotionEvent);
verify(spyMediaController).isShowing();
verify(spyMediaController).hide();
}
@Test
public void onTouchEventMotionActionDownShowsMediaControllerWhenHidden() {
MotionEvent mockMotionEvent = Mockito.mock(MotionEvent.class);
doReturn(MotionEvent.ACTION_DOWN).when(mockMotionEvent).getAction();
doReturn(false).when(spyMediaController).isShowing();
activity.setSerenityMediaController(spyMediaController);
activity.onTouchEvent(mockMotionEvent);
verify(spyMediaController).isShowing();
verify(spyMediaController).show();
}
@Test
public void onTouchEventCallsSuperWhenEventNotHandled() {
MotionEvent mockMotionEvent = Mockito.mock(MotionEvent.class);
doReturn(MotionEvent.ACTION_UP).when(mockMotionEvent).getAction();
activity.setSerenityMediaController(spyMediaController);
activity.onTouchEvent(mockMotionEvent);
verify(spyMediaController, never()).isShowing();
verify(spyMediaController, never()).show();
verify(spyMediaController, never()).hide();
}
@Override
public List<Object> getModules() {
List<Object> modules = new ArrayList<Object>();
modules.add(new AndroidModule(Robolectric.application));
modules.add(new TestModule());
return modules;
}
@Module(addsTo = AndroidModule.class, includes = SerenityModule.class, overrides = true, injects = {
SerenitySurfaceViewVideoActivity.class,
SerenitySurfaceViewVideoPlayerTest.class })
public class TestModule {
@Provides
@ForVideoQueue
@Singleton
LinkedList<VideoContentInfo> providesVideoQueue() {
return videoQueue;
}
@Provides
MediaPlayer providesMediaPlayer() {
return mockMediaPlayer;
}
}
}