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
17package com.android.setupwizardlib.view;
18
19import static org.junit.Assert.assertEquals;
20import static org.junit.Assert.assertNotNull;
21import static org.junit.Assert.fail;
22import static org.mockito.Mockito.mock;
23import static org.mockito.Mockito.reset;
24import static org.mockito.Mockito.verify;
25import static org.robolectric.RuntimeEnvironment.application;
26
27import android.annotation.TargetApi;
28import android.content.Context;
29import android.graphics.SurfaceTexture;
30import android.media.MediaPlayer;
31import android.os.Build.VERSION_CODES;
32import android.support.annotation.RawRes;
33import android.view.Surface;
34
35import com.android.setupwizardlib.R;
36import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
37import com.android.setupwizardlib.shadow.ShadowLog;
38import com.android.setupwizardlib.shadow.ShadowLog.TerribleFailure;
39import com.android.setupwizardlib.view.IllustrationVideoViewTest.ShadowMockMediaPlayer;
40import com.android.setupwizardlib.view.IllustrationVideoViewTest.ShadowSurface;
41
42import org.junit.After;
43import org.junit.Before;
44import org.junit.Test;
45import org.junit.runner.RunWith;
46import org.mockito.Mock;
47import org.mockito.MockitoAnnotations;
48import org.robolectric.Robolectric;
49import org.robolectric.annotation.Config;
50import org.robolectric.annotation.Implementation;
51import org.robolectric.annotation.Implements;
52import org.robolectric.annotation.RealObject;
53import org.robolectric.shadow.api.Shadow;
54import org.robolectric.shadows.ShadowMediaPlayer;
55import org.robolectric.util.ReflectionHelpers;
56
57@RunWith(SuwLibRobolectricTestRunner.class)
58@Config(
59        sdk = Config.NEWEST_SDK,
60        shadows = {
61                ShadowLog.class,
62                ShadowMockMediaPlayer.class,
63                ShadowSurface.class
64        })
65public class IllustrationVideoViewTest {
66
67    @Mock
68    private SurfaceTexture mSurfaceTexture;
69
70    private IllustrationVideoView mView;
71
72    @Before
73    public void setUp() {
74        MockitoAnnotations.initMocks(this);
75    }
76
77    @After
78    public void tearDown() {
79        ShadowMockMediaPlayer.reset();
80    }
81
82    @Test
83    public void nullMediaPlayer_shouldThrowWtf() {
84        ShadowMockMediaPlayer.sMediaPlayer = null;
85        try {
86            createDefaultView();
87            fail("WTF should be thrown for null media player");
88        } catch (TerribleFailure e) {
89            // pass
90        }
91    }
92
93    @Test
94    public void testPausedWhenWindowFocusLost() {
95        createDefaultView();
96        mView.start();
97
98        assertNotNull(mView.mMediaPlayer);
99        assertNotNull(mView.mSurface);
100
101        mView.onWindowFocusChanged(false);
102        verify(ShadowMockMediaPlayer.getMock()).pause();
103    }
104
105    @Test
106    public void testStartedWhenWindowFocusRegained() {
107        testPausedWhenWindowFocusLost();
108
109        // Clear verifications for calls in the other test
110        reset(ShadowMockMediaPlayer.getMock());
111
112        mView.onWindowFocusChanged(true);
113        verify(ShadowMockMediaPlayer.getMock()).start();
114    }
115
116    @Test
117    public void testSurfaceReleasedWhenTextureDestroyed() {
118        createDefaultView();
119        mView.start();
120
121        assertNotNull(mView.mMediaPlayer);
122        assertNotNull(mView.mSurface);
123
124        mView.onSurfaceTextureDestroyed(mSurfaceTexture);
125        verify(ShadowMockMediaPlayer.getMock()).release();
126    }
127
128    @Test
129    public void testXmlSetVideoResId() {
130        createDefaultView();
131        assertEquals(android.R.color.white, ShadowMockMediaPlayer.sResId);
132    }
133
134    @Test
135    public void testSetVideoResId() {
136        createDefaultView();
137
138        @RawRes int black = android.R.color.black;
139        mView.setVideoResource(black);
140
141        assertEquals(android.R.color.black, ShadowMockMediaPlayer.sResId);
142    }
143
144    private void createDefaultView() {
145        mView = new IllustrationVideoView(
146                application,
147                Robolectric.buildAttributeSet()
148                        // Any resource attribute should work, since the media player is mocked
149                        .addAttribute(R.attr.suwVideo, "@android:color/white")
150                        .build());
151        mView.onSurfaceTextureAvailable(mSurfaceTexture, 500, 500);
152    }
153
154    @Implements(MediaPlayer.class)
155    public static class ShadowMockMediaPlayer extends ShadowMediaPlayer {
156
157        private static MediaPlayer sMediaPlayer = mock(MediaPlayer.class);
158        private static int sResId;
159
160        public static void reset() {
161            sMediaPlayer = mock(MediaPlayer.class);
162            sResId = 0;
163        }
164
165        @Implementation
166        public static MediaPlayer create(Context context, int resId) {
167            sResId = resId;
168            return sMediaPlayer;
169        }
170
171        public static MediaPlayer getMock() {
172            return sMediaPlayer;
173        }
174    }
175
176    @Implements(Surface.class)
177    @TargetApi(VERSION_CODES.HONEYCOMB)
178    public static class ShadowSurface extends org.robolectric.shadows.ShadowSurface {
179
180        @RealObject
181        private Surface mRealSurface;
182
183        public void __constructor__(SurfaceTexture surfaceTexture) {
184            // Call the constructor on the real object, so that critical fields such as mLock is
185            // initialized properly.
186            Shadow.invokeConstructor(Surface.class, mRealSurface,
187                    ReflectionHelpers.ClassParameter.from(SurfaceTexture.class, surfaceTexture));
188            super.__constructor__(surfaceTexture);
189        }
190    }
191}
192