1 /*
2  * Copyright (C) 2019 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 package com.android.tv.guide;
17 
18 import static com.google.android.libraries.testing.truth.TextViewSubject.assertThat;
19 
20 import android.support.annotation.NonNull;
21 import android.view.LayoutInflater;
22 
23 import com.android.tv.R;
24 import com.android.tv.common.flags.impl.SettableFlagsModule;
25 import com.android.tv.common.util.Clock;
26 import com.android.tv.data.ChannelDataManager;
27 import com.android.tv.data.ProgramImpl;
28 import com.android.tv.dvr.DvrManager;
29 import com.android.tv.dvr.data.ScheduledRecording;
30 import com.android.tv.guide.ProgramItemViewTest.TestApp;
31 import com.android.tv.guide.ProgramItemViewTest.TestModule.Contributes;
32 import com.android.tv.guide.ProgramManager.TableEntry;
33 import com.android.tv.testing.TestSingletonApp;
34 import com.android.tv.testing.constants.ConfigConstants;
35 import com.android.tv.testing.robo.RobotTestAppHelper;
36 import com.android.tv.testing.testdata.TestData;
37 
38 import dagger.Component;
39 import dagger.Module;
40 import dagger.Provides;
41 import dagger.android.AndroidInjectionModule;
42 import dagger.android.AndroidInjector;
43 import dagger.android.ContributesAndroidInjector;
44 import dagger.android.DispatchingAndroidInjector;
45 import dagger.android.HasAndroidInjector;
46 
47 import org.junit.Before;
48 import org.junit.Test;
49 import org.junit.runner.RunWith;
50 import org.mockito.Mock;
51 import org.mockito.Mockito;
52 import org.mockito.MockitoAnnotations;
53 import org.robolectric.RobolectricTestRunner;
54 import org.robolectric.RuntimeEnvironment;
55 import org.robolectric.annotation.Config;
56 
57 import java.time.Duration;
58 import java.util.concurrent.TimeUnit;
59 
60 import javax.inject.Inject;
61 
62 /** Tests for {@link ProgramItemView}. */
63 @RunWith(RobolectricTestRunner.class)
64 @Config(sdk = ConfigConstants.SDK, application = TestApp.class)
65 public class ProgramItemViewTest {
66 
67     /** TestApp for {@link ProgramItemViewTest} */
68     public static class TestApp extends TestSingletonApp implements HasAndroidInjector {
69         @Inject DispatchingAndroidInjector<Object> dispatchingAndroidInjector;
70 
71         @Override
onCreate()72         public void onCreate() {
73             super.onCreate();
74             DaggerProgramItemViewTest_TestAppComponent.builder()
75                     .testModule(new TestModule(this))
76                     .build()
77                     .inject(this);
78         }
79 
80         @Override
androidInjector()81         public AndroidInjector<Object> androidInjector() {
82             return dispatchingAndroidInjector;
83         }
84     }
85 
86     /** Component for {@link ProgramItemViewTest} */
87     @Component(
88             modules = {
89                 AndroidInjectionModule.class,
90                 TestModule.class,
91             })
92     interface TestAppComponent extends AndroidInjector<TestApp> {}
93 
94     /** Module for {@link ProgramItemViewTest} */
95     @Module(includes = {Contributes.class, SettableFlagsModule.class})
96     public static class TestModule {
97 
98         @Module()
99         public abstract static class Contributes {
100             @ContributesAndroidInjector
contributesProgramItemView()101             abstract ProgramItemView contributesProgramItemView();
102         }
103 
104         private final TestApp myTestApp;
105 
TestModule(TestApp test)106         TestModule(TestApp test) {
107             myTestApp = test;
108         }
109 
110         @Provides
providesChannelDataManager()111         ChannelDataManager providesChannelDataManager() {
112             return myTestApp.getChannelDataManager();
113         }
114 
115         @Provides
provideClock()116         Clock provideClock() {
117             return myTestApp.getClock();
118         }
119     }
120 
121     //  Thursday, June 1, 2017 1:00:00 PM GMT-07:00
122     private final long testStartTimeMs = 1496347200000L;
123 
124     // Thursday, June 1, 2017 8:00:00 PM GMT-07:00
125     private final long eightPM = 1496372400000L;
126     private final ProgramImpl baseProgram =
127             new ProgramImpl.Builder()
128                     .setChannelId(1)
129                     .setStartTimeUtcMillis(eightPM)
130                     .setEndTimeUtcMillis(eightPM + Duration.ofHours(1).toMillis())
131                     .build();
132 
133     private ProgramItemView mPprogramItemView;
134 
135     @Mock DvrManager dvrManager;
136 
137     @Before
setup()138     public void setup() {
139         MockitoAnnotations.initMocks(this);
140         TestSingletonApp app = (TestSingletonApp) RuntimeEnvironment.application;
141         app.dvrManager = dvrManager;
142         app.fakeClock.setBootTimeMillis(testStartTimeMs + TimeUnit.HOURS.toMillis(-12));
143         app.fakeClock.setCurrentTimeMillis(testStartTimeMs);
144         RobotTestAppHelper.loadTestData(app, TestData.DEFAULT_10_CHANNELS);
145         mPprogramItemView =
146                 (ProgramItemView)
147                         LayoutInflater.from(RuntimeEnvironment.application)
148                                 .inflate(R.layout.program_guide_table_item, null);
149         GuideUtils.setWidthPerHour(100);
150     }
151 
152     @Test
initialState()153     public void initialState() {
154         assertThat(mPprogramItemView).hasEmptyText();
155     }
156 
157     @Test
setValue_noProgram()158     public void setValue_noProgram() {
159         TableEntry tableEntry = create30MinuteTableEntryFor(null, null, false);
160         mPprogramItemView.setValues(null, tableEntry, 0, 0, 0, "a gap");
161         assertThat(mPprogramItemView).hasText("a gap");
162         assertThat(mPprogramItemView).hasContentDescription("1 a gap 8:00 – 9:00 PM");
163     }
164 
165     @Test
setValue_programNoTitle()166     public void setValue_programNoTitle() {
167         ProgramImpl program = new ProgramImpl.Builder(baseProgram).build();
168         TableEntry tableEntry = create30MinuteTableEntryFor(program, null, false);
169         mPprogramItemView.setValues(null, tableEntry, 0, 0, 0, "a gap");
170         assertThat(mPprogramItemView).hasText("No information");
171         assertThat(mPprogramItemView).hasContentDescription("1 No information 8:00 – 9:00 PM");
172     }
173 
174     @Test
setValue_programTitle()175     public void setValue_programTitle() {
176         ProgramImpl program =
177                 new ProgramImpl.Builder(baseProgram).setTitle("A good program").build();
178         TableEntry tableEntry = create30MinuteTableEntryFor(program, null, false);
179         mPprogramItemView.setValues(null, tableEntry, 0, 0, 0, "a gap");
180         assertThat(mPprogramItemView).hasText("A good program");
181         assertThat(mPprogramItemView).hasContentDescription("1 A good program 8:00 – 9:00 PM");
182     }
183 
184     @Test
setValue_programDescriptionBlocked()185     public void setValue_programDescriptionBlocked() {
186         ProgramImpl program =
187                 new ProgramImpl.Builder(baseProgram)
188                         .setTitle("A good program")
189                         .setDescription("Naughty")
190                         .build();
191         TableEntry tableEntry = create30MinuteTableEntryFor(program, null, true);
192         mPprogramItemView.setValues(null, tableEntry, 0, 0, 0, "a gap");
193         assertThat(mPprogramItemView).hasText("A good program");
194         assertThat(mPprogramItemView)
195                 .hasContentDescription("1 A good program 8:00 – 9:00 PM This program is blocked");
196     }
197 
198     @Test
setValue_programEpisode()199     public void setValue_programEpisode() {
200         ProgramImpl program =
201                 new ProgramImpl.Builder(baseProgram)
202                         .setTitle("A good program")
203                         .setEpisodeTitle("The one with an episode")
204                         .build();
205         TableEntry tableEntry = create30MinuteTableEntryFor(program, null, false);
206         mPprogramItemView.setValues(null, tableEntry, 0, 0, 0, "a gap");
207         assertThat(mPprogramItemView).hasText("A good program\n\u200DThe one with an episode");
208         assertThat(mPprogramItemView)
209                 .hasContentDescription("1 A good program 8:00 – 9:00 PM The one with an episode");
210     }
211 
212     @Test
setValue_programEpisodeAndDescrition()213     public void setValue_programEpisodeAndDescrition() {
214         ProgramImpl program =
215                 new ProgramImpl.Builder(baseProgram)
216                         .setTitle("A good program")
217                         .setEpisodeTitle("The one with an episode")
218                         .setDescription("Jack and Jill go up a hill")
219                         .build();
220         TableEntry tableEntry = create30MinuteTableEntryFor(program, null, false);
221         mPprogramItemView.setValues(null, tableEntry, 0, 0, 0, "a gap");
222         assertThat(mPprogramItemView).hasText("A good program\n\u200DThe one with an episode");
223         assertThat(mPprogramItemView)
224                 .hasContentDescription(
225                         "1 A good program 8:00 – 9:00 PM The one with an episode"
226                                 + " Jack and Jill go up a hill");
227     }
228 
229     @Test
setValue_scheduledConflict()230     public void setValue_scheduledConflict() {
231         ProgramImpl program =
232                 new ProgramImpl.Builder(baseProgram).setTitle("A good program").build();
233         ScheduledRecording scheduledRecording =
234                 ScheduledRecording.builder("input1", program).build();
235         TableEntry tableEntry = create30MinuteTableEntryFor(program, scheduledRecording, false);
236         Mockito.when(dvrManager.isConflicting(scheduledRecording)).thenReturn(true);
237 
238         mPprogramItemView.setValues(null, tableEntry, 0, 0, 0, "a gap");
239         assertThat(mPprogramItemView).hasText("A good program");
240         assertThat(mPprogramItemView)
241                 .hasContentDescription("1 A good program 8:00 – 9:00 PM Recording conflict");
242     }
243 
244     @Test
setValue_scheduled()245     public void setValue_scheduled() {
246         ProgramImpl program =
247                 new ProgramImpl.Builder(baseProgram).setTitle("A good program").build();
248         ScheduledRecording scheduledRecording =
249                 ScheduledRecording.builder("input1", program)
250                         .setState(ScheduledRecording.STATE_RECORDING_NOT_STARTED)
251                         .build();
252         TableEntry tableEntry = create30MinuteTableEntryFor(program, scheduledRecording, false);
253         Mockito.when(dvrManager.isConflicting(scheduledRecording)).thenReturn(false);
254 
255         mPprogramItemView.setValues(null, tableEntry, 0, 0, 0, "a gap");
256         assertThat(mPprogramItemView).hasText("A good program");
257         assertThat(mPprogramItemView)
258                 .hasContentDescription("1 A good program 8:00 – 9:00 PM Recording scheduled");
259     }
260 
261     @Test
setValue_recordingInProgress()262     public void setValue_recordingInProgress() {
263         ProgramImpl program =
264                 new ProgramImpl.Builder(baseProgram).setTitle("A good program").build();
265         ScheduledRecording scheduledRecording =
266                 ScheduledRecording.builder("input1", program)
267                         .setState(ScheduledRecording.STATE_RECORDING_IN_PROGRESS)
268                         .build();
269         TableEntry tableEntry = create30MinuteTableEntryFor(program, scheduledRecording, false);
270         Mockito.when(dvrManager.isConflicting(scheduledRecording)).thenReturn(false);
271 
272         mPprogramItemView.setValues(null, tableEntry, 0, 0, 0, "a gap");
273         assertThat(mPprogramItemView).hasText("A good program");
274         assertThat(mPprogramItemView)
275                 .hasContentDescription("1 A good program 8:00 – 9:00 PM Recording");
276     }
277 
278     @NonNull
create30MinuteTableEntryFor( ProgramImpl program, ScheduledRecording scheduledRecording, boolean isBlocked)279     private TableEntry create30MinuteTableEntryFor(
280             ProgramImpl program, ScheduledRecording scheduledRecording, boolean isBlocked) {
281         return ProgramManager.createTableEntryForTest(
282                 1,
283                 program,
284                 scheduledRecording,
285                 eightPM,
286                 eightPM + Duration.ofHours(1).toMillis(),
287                 isBlocked);
288     }
289 }
290