1 /* 2 * Copyright (C) 2017 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 com.android.setupwizardlib.view; 18 19 import static com.google.common.truth.Truth.assertThat; 20 import static org.mockito.Mockito.mock; 21 import static org.robolectric.RuntimeEnvironment.application; 22 23 import android.app.Activity; 24 import android.graphics.SurfaceTexture; 25 import android.net.Uri; 26 import androidx.annotation.RawRes; 27 import android.view.View; 28 import com.android.setupwizardlib.R; 29 import org.junit.Before; 30 import org.junit.Test; 31 import org.junit.runner.RunWith; 32 import org.mockito.Mock; 33 import org.mockito.MockitoAnnotations; 34 import org.robolectric.Robolectric; 35 import org.robolectric.RobolectricTestRunner; 36 import org.robolectric.Shadows; 37 import org.robolectric.annotation.Config; 38 import org.robolectric.shadows.ShadowMediaPlayer; 39 import org.robolectric.shadows.ShadowMediaPlayer.InvalidStateBehavior; 40 import org.robolectric.shadows.ShadowMediaPlayer.MediaInfo; 41 import org.robolectric.shadows.util.DataSource; 42 import org.robolectric.util.ReflectionHelpers; 43 import org.robolectric.util.ReflectionHelpers.ClassParameter; 44 45 @RunWith(RobolectricTestRunner.class) 46 @Config(sdk = Config.NEWEST_SDK) 47 public class IllustrationVideoViewTest { 48 49 @Mock private SurfaceTexture surfaceTexture; 50 51 private IllustrationVideoView view; 52 53 @Before setUp()54 public void setUp() { 55 MockitoAnnotations.initMocks(this); 56 ShadowMediaPlayer.addMediaInfo( 57 DataSource.toDataSource( 58 "android.resource://" + application.getPackageName() + "/" + android.R.color.white), 59 new ShadowMediaPlayer.MediaInfo(100, 10)); 60 ShadowMediaPlayer.addMediaInfo( 61 DataSource.toDataSource( 62 "android.resource://" + application.getPackageName() + "/" + android.R.color.black), 63 new ShadowMediaPlayer.MediaInfo(100, 10)); 64 } 65 66 @Test testPausedWhenWindowFocusLost()67 public void testPausedWhenWindowFocusLost() { 68 createDefaultView(); 69 Robolectric.flushForegroundThreadScheduler(); 70 view.start(); 71 72 assertThat(view.mMediaPlayer).isNotNull(); 73 assertThat(view.surface).isNotNull(); 74 75 view.onWindowFocusChanged(false); 76 assertThat(getShadowMediaPlayer().getState()).isEqualTo(ShadowMediaPlayer.State.PAUSED); 77 } 78 79 @Test testStartedWhenWindowFocusRegained()80 public void testStartedWhenWindowFocusRegained() { 81 testPausedWhenWindowFocusLost(); 82 Robolectric.flushForegroundThreadScheduler(); 83 84 view.onWindowFocusChanged(true); 85 assertThat(getShadowMediaPlayer().getState()).isEqualTo(ShadowMediaPlayer.State.STARTED); 86 } 87 88 @Test testSurfaceReleasedWhenTextureDestroyed()89 public void testSurfaceReleasedWhenTextureDestroyed() { 90 createDefaultView(); 91 view.start(); 92 93 assertThat(view.mMediaPlayer).isNotNull(); 94 assertThat(view.surface).isNotNull(); 95 96 // MediaPlayer is set to null after destroy. Retrieve it first before we call destroy. 97 ShadowMediaPlayer shadowMediaPlayer = getShadowMediaPlayer(); 98 view.onSurfaceTextureDestroyed(surfaceTexture); 99 assertThat(shadowMediaPlayer.getState()).isEqualTo(ShadowMediaPlayer.State.END); 100 } 101 102 @Test testXmlSetVideoResId()103 public void testXmlSetVideoResId() { 104 createDefaultView(); 105 assertThat(getShadowMediaPlayer().getSourceUri().toString()) 106 .isEqualTo("android.resource://com.android.setupwizardlib/" + android.R.color.white); 107 } 108 109 @Test testSetVideoResId()110 public void testSetVideoResId() { 111 createDefaultView(); 112 113 @RawRes int black = android.R.color.black; 114 view.setVideoResource(black); 115 116 assertThat(getShadowMediaPlayer().getSourceUri().toString()) 117 .isEqualTo("android.resource://com.android.setupwizardlib/" + android.R.color.black); 118 } 119 120 @Test prepareVideo_shouldSetAspectRatio()121 public void prepareVideo_shouldSetAspectRatio() { 122 createDefaultView(); 123 124 ReflectionHelpers.setField(getShadowMediaPlayer(), "videoWidth", 720); 125 ReflectionHelpers.setField(getShadowMediaPlayer(), "videoHeight", 1280); 126 127 Robolectric.flushForegroundThreadScheduler(); 128 view.start(); 129 130 view.measure( 131 View.MeasureSpec.makeMeasureSpec(720, View.MeasureSpec.EXACTLY), 132 View.MeasureSpec.makeMeasureSpec(720, View.MeasureSpec.EXACTLY)); 133 134 final float aspectRatio = (float) view.getMeasuredHeight() / view.getMeasuredWidth(); 135 assertThat(aspectRatio).isWithin(0.001f).of(1280f / 720f); 136 } 137 138 @Test prepareVideo_zeroHeight_shouldSetAspectRatioToZero()139 public void prepareVideo_zeroHeight_shouldSetAspectRatioToZero() { 140 createDefaultView(); 141 142 ReflectionHelpers.setField(getShadowMediaPlayer(), "videoWidth", 720); 143 ReflectionHelpers.setField(getShadowMediaPlayer(), "videoHeight", 0); 144 145 Robolectric.flushForegroundThreadScheduler(); 146 view.start(); 147 148 final float aspectRatio = (float) view.getHeight() / view.getWidth(); 149 assertThat(aspectRatio).isEqualTo(0.0f); 150 } 151 152 @Test setVideoResId_resetDiffVideoResFromDiffPackage_videoResShouldBeSet()153 public void setVideoResId_resetDiffVideoResFromDiffPackage_videoResShouldBeSet() { 154 // VideoRes default set as android.R.color.white with 155 // default package(com.android.setupwizardlib) 156 createDefaultView(); 157 158 // reset different videoRes from different package 159 String newPackageName = "com.android.fakepackage"; 160 @RawRes int black = android.R.color.black; 161 addMediaInfo(black, newPackageName); 162 view.setVideoResource(black, newPackageName); 163 164 // should be reset to black with the new package 165 assertThat(getShadowMediaPlayer().getSourceUri().toString()) 166 .isEqualTo("android.resource://" + newPackageName + "/" + android.R.color.black); 167 } 168 169 @Test setVideoResId_resetDiffVideoResFromSamePackage_videoResShouldBeSet()170 public void setVideoResId_resetDiffVideoResFromSamePackage_videoResShouldBeSet() { 171 // VideoRes default set as android.R.color.white with 172 // default package(com.android.setupwizardlib) 173 createDefaultView(); 174 175 // reset different videoRes from the same package(default package) 176 String defaultPackageName = "com.android.setupwizardlib"; 177 @RawRes int black = android.R.color.black; 178 addMediaInfo(black, defaultPackageName); 179 view.setVideoResource(black, defaultPackageName); 180 181 // should be reset to black with the same package(default package) 182 assertThat(getShadowMediaPlayer().getSourceUri().toString()) 183 .isEqualTo("android.resource://" + defaultPackageName + "/" + android.R.color.black); 184 } 185 186 @Test setVideoResId_resetSameVideoResFromDifferentPackage_videoResShouldBeSet()187 public void setVideoResId_resetSameVideoResFromDifferentPackage_videoResShouldBeSet() { 188 // VideoRes default set as android.R.color.white with 189 // default package(com.android.setupwizardlib) 190 createDefaultView(); 191 192 // reset same videoRes from different package 193 @RawRes int white = android.R.color.white; 194 String newPackageName = "com.android.fakepackage"; 195 addMediaInfo(white, newPackageName); 196 view.setVideoResource(white, newPackageName); 197 198 // should be white with the new package 199 assertThat(getShadowMediaPlayer().getSourceUri().toString()) 200 .isEqualTo("android.resource://" + newPackageName + "/" + android.R.color.white); 201 } 202 getShadowMediaPlayer()203 private ShadowMediaPlayer getShadowMediaPlayer() { 204 return Shadows.shadowOf(view.mMediaPlayer); 205 } 206 createDefaultView()207 private void createDefaultView() { 208 view = 209 new IllustrationVideoView( 210 application, 211 Robolectric.buildAttributeSet() 212 // Any resource attribute should work, since the data source is fake 213 .addAttribute(R.attr.suwVideo, "@android:color/white") 214 .build()); 215 216 Activity activity = Robolectric.setupActivity(Activity.class); 217 activity.setContentView(view); 218 setWindowVisible(); 219 220 view.setSurfaceTexture(mock(SurfaceTexture.class)); 221 view.onSurfaceTextureAvailable(surfaceTexture, 500, 500); 222 getShadowMediaPlayer().setInvalidStateBehavior(InvalidStateBehavior.EMULATE); 223 } 224 setWindowVisible()225 private void setWindowVisible() { 226 Object viewRootImpl = ReflectionHelpers.callInstanceMethod(view, "getViewRootImpl"); 227 ReflectionHelpers.callInstanceMethod( 228 viewRootImpl, "handleAppVisibility", ClassParameter.from(boolean.class, true)); 229 assertThat(view.isAttachedToWindow()).isTrue(); 230 assertThat(view.getWindowVisibility()).isEqualTo(View.VISIBLE); 231 } 232 addMediaInfo(@awRes int res, String packageName)233 private void addMediaInfo(@RawRes int res, String packageName) { 234 ShadowMediaPlayer.addMediaInfo( 235 DataSource.toDataSource( 236 application, Uri.parse("android.resource://" + packageName + "/" + res), null), 237 new MediaInfo(5000, 1)); 238 } 239 } 240