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