From 5bd435e6c45c7776e18a96141cdc0a952ad45608 Mon Sep 17 00:00:00 2001 From: Peter_Liang Date: Mon, 28 Dec 2020 16:45:38 +0800 Subject: [PATCH] Rename and add test cases for video player in accessibility. Bug: 168567356 Test: make RunSettingsRoboTests ROBOTEST_FILTER=VideoPlayerTest Change-Id: I2ea415b06d2870f55650290f48dd9b2998ef608d --- .../settings/accessibility/VideoPlayer.java | 71 ++++++----- .../accessibility/VideoPlayerTest.java | 114 ++++++++++++++++++ 2 files changed, 153 insertions(+), 32 deletions(-) create mode 100644 tests/robotests/src/com/android/settings/accessibility/VideoPlayerTest.java diff --git a/src/com/android/settings/accessibility/VideoPlayer.java b/src/com/android/settings/accessibility/VideoPlayer.java index 8f94b768f67..d4aa6a6a69b 100644 --- a/src/com/android/settings/accessibility/VideoPlayer.java +++ b/src/com/android/settings/accessibility/VideoPlayer.java @@ -25,6 +25,7 @@ import android.view.TextureView.SurfaceTextureListener; import androidx.annotation.GuardedBy; import androidx.annotation.RawRes; +import androidx.annotation.VisibleForTesting; /** * Plays the video by {@link MediaPlayer} on {@link TextureView}, calls {@link #create(Context, int, @@ -32,19 +33,25 @@ import androidx.annotation.RawRes; * is no longer used, call {@link #release()} so that MediaPlayer object can be released. */ public class VideoPlayer implements SurfaceTextureListener { - private final Context context; - private final Object mediaPlayerLock = new Object(); + private final Context mContext; + private final Object mMediaPlayerLock = new Object(); // Media player object can't be used after it has been released, so it will be set to null. But // VideoPlayer is asynchronized, media player object might be paused or resumed again before // released media player is set to null. Therefore, lock mediaPlayer and mediaPlayerState by // mediaPlayerLock keep their states consistent. + @VisibleForTesting @GuardedBy("mediaPlayerLock") - private MediaPlayer mediaPlayer; + MediaPlayer mMediaPlayer; + + @VisibleForTesting @GuardedBy("mediaPlayerLock") - private State mediaPlayerState = State.NONE; + State mMediaPlayerState = State.NONE; + @RawRes - private final int videoRes; - private Surface animationSurface; + private final int mVideoRes; + + @VisibleForTesting + Surface mAnimationSurface; /** @@ -58,54 +65,54 @@ public class VideoPlayer implements SurfaceTextureListener { } private VideoPlayer(Context context, @RawRes int videoRes, TextureView textureView) { - this.context = context; - this.videoRes = videoRes; + this.mContext = context; + this.mVideoRes = videoRes; textureView.setSurfaceTextureListener(this); } public void pause() { - synchronized (mediaPlayerLock) { - if (mediaPlayerState == State.STARTED) { - mediaPlayerState = State.PAUSED; - mediaPlayer.pause(); + synchronized (mMediaPlayerLock) { + if (mMediaPlayerState == State.STARTED) { + mMediaPlayerState = State.PAUSED; + mMediaPlayer.pause(); } } } public void resume() { - synchronized (mediaPlayerLock) { - if (mediaPlayerState == State.PAUSED) { - mediaPlayer.start(); - mediaPlayerState = State.STARTED; + synchronized (mMediaPlayerLock) { + if (mMediaPlayerState == State.PAUSED) { + mMediaPlayer.start(); + mMediaPlayerState = State.STARTED; } } } /** Release media player when it's no longer needed. */ public void release() { - synchronized (mediaPlayerLock) { - if (mediaPlayerState != State.NONE && mediaPlayerState != State.END) { - mediaPlayerState = State.END; - mediaPlayer.release(); - mediaPlayer = null; + synchronized (mMediaPlayerLock) { + if (mMediaPlayerState != State.NONE && mMediaPlayerState != State.END) { + mMediaPlayerState = State.END; + mMediaPlayer.release(); + mMediaPlayer = null; } } - if (animationSurface != null) { - animationSurface.release(); - animationSurface = null; + if (mAnimationSurface != null) { + mAnimationSurface.release(); + mAnimationSurface = null; } } @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { - animationSurface = new Surface(surface); - synchronized (mediaPlayerLock) { - mediaPlayer = MediaPlayer.create(context, videoRes); - mediaPlayerState = State.PREPARED; - mediaPlayer.setSurface(animationSurface); - mediaPlayer.setLooping(true); - mediaPlayer.start(); - mediaPlayerState = State.STARTED; + mAnimationSurface = new Surface(surface); + synchronized (mMediaPlayerLock) { + mMediaPlayer = MediaPlayer.create(mContext, mVideoRes); + mMediaPlayerState = State.PREPARED; + mMediaPlayer.setSurface(mAnimationSurface); + mMediaPlayer.setLooping(true); + mMediaPlayer.start(); + mMediaPlayerState = State.STARTED; } } diff --git a/tests/robotests/src/com/android/settings/accessibility/VideoPlayerTest.java b/tests/robotests/src/com/android/settings/accessibility/VideoPlayerTest.java new file mode 100644 index 00000000000..57df456bf13 --- /dev/null +++ b/tests/robotests/src/com/android/settings/accessibility/VideoPlayerTest.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 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 com.android.settings.accessibility; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.media.MediaPlayer; +import android.view.Surface; +import android.view.TextureView; + +import androidx.test.core.app.ApplicationProvider; + +import com.android.settings.accessibility.VideoPlayer.State; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; + +/** Tests for {@link VideoPlayer}. */ +@RunWith(RobolectricTestRunner.class) +public class VideoPlayerTest { + + @Mock + private MediaPlayer mMediaPlayer; + + @Mock + private TextureView mTextureView; + + @Mock + private Surface mSurface; + + private VideoPlayer mVideoPlayer; + + @Before + public void initVideoPlayer() { + MockitoAnnotations.initMocks(this); + + final int videoRes = 0; + final Context context = ApplicationProvider.getApplicationContext(); + + mVideoPlayer = spy(VideoPlayer.create(context, videoRes, mTextureView)); + mVideoPlayer.mMediaPlayer = mMediaPlayer; + mVideoPlayer.mAnimationSurface = mSurface; + } + + @Test + public void setSurfaceTextureListener_success() { + verify(mTextureView).setSurfaceTextureListener(any()); + } + + @Test + public void onPlayerPaused_startedState_pause() { + mVideoPlayer.mMediaPlayerState = State.STARTED; + + mVideoPlayer.pause(); + + assertThat(mVideoPlayer.mMediaPlayerState).isEqualTo(State.PAUSED); + verify(mMediaPlayer).pause(); + } + + @Test + public void onPlayerResumed_pausedState_start() { + mVideoPlayer.mMediaPlayerState = State.PAUSED; + + mVideoPlayer.resume(); + + assertThat(mVideoPlayer.mMediaPlayerState).isEqualTo(State.STARTED); + verify(mMediaPlayer).start(); + } + + @Test + public void onPlayerReleased_stoppedState_release() { + mVideoPlayer.mMediaPlayerState = State.STOPPED; + + mVideoPlayer.release(); + + assertThat(mVideoPlayer.mMediaPlayerState).isEqualTo(State.END); + verify(mMediaPlayer).release(); + verify(mSurface).release(); + } + + @Test + public void onSurfaceTextureDestroyed_preparedState_release() { + mVideoPlayer.mMediaPlayerState = State.PREPARED; + + mVideoPlayer.onSurfaceTextureDestroyed(any()); + + assertThat(mVideoPlayer.mMediaPlayerState).isEqualTo(State.END); + verify(mMediaPlayer).release(); + verify(mSurface).release(); + } +}