1 /* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.widget.cts; 18 19 import static com.android.compatibility.common.util.CtsMockitoUtils.within; 20 21 import static org.junit.Assert.assertEquals; 22 import static org.junit.Assert.assertNotNull; 23 import static org.junit.Assert.assertTrue; 24 import static org.mockito.Matchers.any; 25 import static org.mockito.Matchers.anyInt; 26 import static org.mockito.Mockito.mock; 27 import static org.mockito.Mockito.times; 28 import static org.mockito.Mockito.verify; 29 import static org.mockito.Mockito.verifyZeroInteractions; 30 31 import android.app.Activity; 32 import android.app.Instrumentation; 33 import android.content.Context; 34 import android.media.AudioAttributes; 35 import android.media.AudioManager; 36 import android.media.AudioPlaybackConfiguration; 37 import android.media.MediaPlayer; 38 import android.os.SystemClock; 39 import android.support.test.InstrumentationRegistry; 40 import android.support.test.annotation.UiThreadTest; 41 import android.support.test.filters.LargeTest; 42 import android.support.test.rule.ActivityTestRule; 43 import android.support.test.runner.AndroidJUnit4; 44 import android.util.Log; 45 import android.view.View.MeasureSpec; 46 import android.widget.MediaController; 47 import android.widget.VideoView; 48 49 import com.android.compatibility.common.util.MediaUtils; 50 51 import org.junit.Before; 52 import org.junit.Rule; 53 import org.junit.Test; 54 import org.junit.runner.RunWith; 55 56 import java.io.IOException; 57 import java.io.InputStream; 58 import java.io.OutputStream; 59 import java.util.List; 60 61 /** 62 * Test {@link VideoView}. 63 */ 64 @LargeTest 65 @RunWith(AndroidJUnit4.class) 66 public class VideoViewTest { 67 /** Debug TAG. **/ 68 private static final String TAG = "VideoViewTest"; 69 /** The maximum time to wait for an operation. */ 70 private static final long TIME_OUT = 15000L; 71 /** The interval time to wait for completing an operation. */ 72 private static final long OPERATION_INTERVAL = 1500L; 73 /** The duration of R.raw.testvideo. */ 74 private static final int TEST_VIDEO_DURATION = 11047; 75 /** The full name of R.raw.testvideo. */ 76 private static final String VIDEO_NAME = "testvideo.3gp"; 77 /** delta for duration in case user uses different decoders on different 78 hardware that report a duration that's different by a few milliseconds */ 79 private static final int DURATION_DELTA = 100; 80 /** AudioAttributes to be used by this player */ 81 private static final AudioAttributes AUDIO_ATTR = new AudioAttributes.Builder() 82 .setUsage(AudioAttributes.USAGE_GAME) 83 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) 84 .build(); 85 86 private Instrumentation mInstrumentation; 87 private Activity mActivity; 88 private VideoView mVideoView; 89 private String mVideoPath; 90 91 @Rule 92 public ActivityTestRule<VideoViewCtsActivity> mActivityRule = 93 new ActivityTestRule<>(VideoViewCtsActivity.class); 94 95 @Before 96 public void setup() throws Throwable { 97 mInstrumentation = InstrumentationRegistry.getInstrumentation(); 98 mActivity = mActivityRule.getActivity(); 99 mVideoView = (VideoView) mActivity.findViewById(R.id.videoview); 100 101 mVideoPath = prepareSampleVideo(); 102 assertNotNull(mVideoPath); 103 } 104 105 private boolean hasCodec() { 106 return MediaUtils.hasCodecsForResource(mActivity, R.raw.testvideo); 107 } 108 109 private String prepareSampleVideo() throws IOException { 110 try (InputStream source = mActivity.getResources().openRawResource(R.raw.testvideo); 111 OutputStream target = mActivity.openFileOutput(VIDEO_NAME, Context.MODE_PRIVATE)) { 112 final byte[] buffer = new byte[1024]; 113 for (int len = source.read(buffer); len > 0; len = source.read(buffer)) { 114 target.write(buffer, 0, len); 115 } 116 } 117 118 return mActivity.getFileStreamPath(VIDEO_NAME).getAbsolutePath(); 119 } 120 121 private void makeVideoView() throws Throwable { 122 mActivityRule.runOnUiThread(() -> { 123 MediaController mediaController = new MediaController(mActivity); 124 mVideoView.setMediaController(mediaController); 125 }); 126 mInstrumentation.waitForIdleSync(); 127 } 128 129 @UiThreadTest 130 @Test 131 public void testConstructor() { 132 new VideoView(mActivity); 133 134 new VideoView(mActivity, null); 135 136 new VideoView(mActivity, null, 0); 137 } 138 139 @Test 140 public void testPlayVideo() throws Throwable { 141 makeVideoView(); 142 // Don't run the test if the codec isn't supported. 143 if (!hasCodec()) { 144 Log.i(TAG, "SKIPPING testPlayVideo1(): codec is not supported"); 145 return; 146 } 147 148 final MediaPlayer.OnPreparedListener mockPreparedListener = 149 mock(MediaPlayer.OnPreparedListener.class); 150 mVideoView.setOnPreparedListener(mockPreparedListener); 151 152 final MediaPlayer.OnCompletionListener mockCompletionListener = 153 mock(MediaPlayer.OnCompletionListener.class); 154 mVideoView.setOnCompletionListener(mockCompletionListener); 155 156 mActivityRule.runOnUiThread(() -> mVideoView.setVideoPath(mVideoPath)); 157 verify(mockPreparedListener, within(TIME_OUT)).onPrepared(any(MediaPlayer.class)); 158 verify(mockPreparedListener, times(1)).onPrepared(any(MediaPlayer.class)); 159 verifyZeroInteractions(mockCompletionListener); 160 161 mActivityRule.runOnUiThread(mVideoView::start); 162 // wait time is longer than duration in case system is sluggish 163 verify(mockCompletionListener, within(TIME_OUT)).onCompletion(any(MediaPlayer.class)); 164 verify(mockCompletionListener, times(1)).onCompletion(any(MediaPlayer.class)); 165 } 166 167 private static final class MyPlaybackCallback extends AudioManager.AudioPlaybackCallback { 168 boolean mMatchingPlayerFound = false; 169 170 @Override 171 public void onPlaybackConfigChanged(List<AudioPlaybackConfiguration> configs) { 172 for (AudioPlaybackConfiguration apc : configs) { 173 if (apc.getAudioAttributes().getUsage() == AUDIO_ATTR.getUsage() 174 && apc.getAudioAttributes().getContentType() 175 == AUDIO_ATTR.getContentType()) { 176 mMatchingPlayerFound = true; 177 break; 178 } 179 } 180 } 181 } 182 183 @Test 184 public void testAudioAttributes() throws Throwable { 185 makeVideoView(); 186 // Don't run the test if the codec isn't supported. 187 if (!hasCodec()) { 188 Log.i(TAG, "SKIPPING testAudioAttributes(): codec is not supported"); 189 return; 190 } 191 192 final MediaPlayer.OnCompletionListener mockCompletionListener = 193 mock(MediaPlayer.OnCompletionListener.class); 194 mVideoView.setOnCompletionListener(mockCompletionListener); 195 196 mVideoView.setAudioAttributes(AUDIO_ATTR); 197 mVideoView.setAudioFocusRequest(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); 198 199 final AudioManager am = (AudioManager) mActivity.getSystemService(Context.AUDIO_SERVICE); 200 final MyPlaybackCallback myCb = new MyPlaybackCallback(); 201 mActivityRule.runOnUiThread(() -> am.registerAudioPlaybackCallback(myCb, null)); 202 mActivityRule.runOnUiThread(() -> mVideoView.setVideoPath(mVideoPath)); 203 mActivityRule.runOnUiThread(mVideoView::start); 204 // wait time is longer than duration in case system is sluggish 205 verify(mockCompletionListener, within(TIME_OUT)).onCompletion(any(MediaPlayer.class)); 206 verify(mockCompletionListener, times(1)).onCompletion(any(MediaPlayer.class)); 207 208 // TODO is there a more compact way to test this with mockito? 209 assertTrue("Audio playback configuration not found for VideoView", 210 myCb.mMatchingPlayerFound); 211 } 212 213 @Test 214 public void testSetOnErrorListener() throws Throwable { 215 makeVideoView(); 216 217 final MediaPlayer.OnErrorListener mockErrorListener = 218 mock(MediaPlayer.OnErrorListener.class); 219 mVideoView.setOnErrorListener(mockErrorListener); 220 221 mActivityRule.runOnUiThread(() -> { 222 String path = "unknown path"; 223 mVideoView.setVideoPath(path); 224 mVideoView.start(); 225 }); 226 mInstrumentation.waitForIdleSync(); 227 228 verify(mockErrorListener, within(TIME_OUT)).onError( 229 any(MediaPlayer.class), anyInt(), anyInt()); 230 verify(mockErrorListener, times(1)).onError(any(MediaPlayer.class), anyInt(), anyInt()); 231 } 232 233 @Test 234 public void testGetBufferPercentage() throws Throwable { 235 makeVideoView(); 236 // Don't run the test if the codec isn't supported. 237 if (!hasCodec()) { 238 Log.i(TAG, "SKIPPING testGetBufferPercentage(): codec is not supported"); 239 return; 240 } 241 242 final MediaPlayer.OnPreparedListener mockPreparedListener = 243 mock(MediaPlayer.OnPreparedListener.class); 244 mVideoView.setOnPreparedListener(mockPreparedListener); 245 246 mActivityRule.runOnUiThread(() -> mVideoView.setVideoPath(mVideoPath)); 247 mInstrumentation.waitForIdleSync(); 248 249 verify(mockPreparedListener, within(TIME_OUT)).onPrepared(any(MediaPlayer.class)); 250 verify(mockPreparedListener, times(1)).onPrepared(any(MediaPlayer.class)); 251 int percent = mVideoView.getBufferPercentage(); 252 assertTrue(percent >= 0 && percent <= 100); 253 } 254 255 @UiThreadTest 256 @Test 257 public void testResolveAdjustedSize() { 258 mVideoView = new VideoView(mActivity); 259 260 final int desiredSize = 100; 261 int resolvedSize = mVideoView.resolveAdjustedSize(desiredSize, MeasureSpec.UNSPECIFIED); 262 assertEquals(desiredSize, resolvedSize); 263 264 final int specSize = MeasureSpec.getSize(MeasureSpec.AT_MOST); 265 resolvedSize = mVideoView.resolveAdjustedSize(desiredSize, MeasureSpec.AT_MOST); 266 assertEquals(Math.min(desiredSize, specSize), resolvedSize); 267 268 resolvedSize = mVideoView.resolveAdjustedSize(desiredSize, MeasureSpec.EXACTLY); 269 assertEquals(specSize, resolvedSize); 270 } 271 272 @Test 273 public void testGetDuration() throws Throwable { 274 // Don't run the test if the codec isn't supported. 275 if (!hasCodec()) { 276 Log.i(TAG, "SKIPPING testGetDuration(): codec is not supported"); 277 return; 278 } 279 280 mActivityRule.runOnUiThread(() -> mVideoView.setVideoPath(mVideoPath)); 281 SystemClock.sleep(OPERATION_INTERVAL); 282 assertTrue(Math.abs(mVideoView.getDuration() - TEST_VIDEO_DURATION) < DURATION_DELTA); 283 } 284 285 @UiThreadTest 286 @Test 287 public void testSetMediaController() { 288 final MediaController ctlr = new MediaController(mActivity); 289 mVideoView.setMediaController(ctlr); 290 } 291 } 292