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.tv.data.epg; 18 19 /** Tests for {@link EpgFetcher}. */ 20 import static com.google.common.truth.Truth.assertThat; 21 22 import android.content.ContentResolver; 23 import android.content.ContentValues; 24 import android.database.sqlite.SQLiteDatabase; 25 import android.media.tv.TvContract; 26 27 import androidx.tvprovider.media.tv.Channel; 28 29 import com.android.tv.common.CommonPreferences; 30 import com.android.tv.common.buildtype.HasBuildType.BuildType; 31 import com.android.tv.common.flags.impl.DefaultBackendKnobsFlags; 32 import com.android.tv.common.flags.impl.SettableFlagsModule; 33 import com.android.tv.common.util.PostalCodeUtils; 34 import com.android.tv.data.ChannelDataManager; 35 import com.android.tv.features.TvFeatures; 36 import com.android.tv.perf.PerformanceMonitor; 37 import com.android.tv.perf.stub.StubPerformanceMonitor; 38 import com.android.tv.testing.DbTestingUtils; 39 import com.android.tv.testing.EpgTestData; 40 import com.android.tv.testing.FakeEpgReader; 41 import com.android.tv.testing.FakeTvInputManagerHelper; 42 import com.android.tv.testing.TestSingletonApp; 43 import com.android.tv.testing.constants.ConfigConstants; 44 import com.android.tv.testing.fakes.FakeClock; 45 import com.android.tv.testing.fakes.FakeTvProvider; 46 import com.android.tv.testing.robo.ContentProviders; 47 48 import com.google.android.tv.livechannels.epg.provider.EpgContentProvider; 49 import com.google.android.tv.partner.support.EpgContract; 50 import com.google.common.collect.ImmutableList; 51 import com.android.tv.common.flags.proto.TypedFeatures.StringListParam; 52 53 import dagger.Component; 54 import dagger.Module; 55 import dagger.android.AndroidInjectionModule; 56 import dagger.android.AndroidInjector; 57 import dagger.android.DispatchingAndroidInjector; 58 import dagger.android.HasAndroidInjector; 59 60 import org.junit.After; 61 import org.junit.Before; 62 import org.junit.Test; 63 import org.junit.runner.RunWith; 64 import org.robolectric.RobolectricTestRunner; 65 import org.robolectric.RuntimeEnvironment; 66 import org.robolectric.Shadows; 67 import org.robolectric.android.util.concurrent.RoboExecutorService; 68 import org.robolectric.annotation.Config; 69 70 import java.util.List; 71 import java.util.concurrent.ExecutionException; 72 import java.util.concurrent.TimeUnit; 73 74 import javax.inject.Inject; 75 76 /** Tests for {@link EpgFetcherImpl}. */ 77 @RunWith(RobolectricTestRunner.class) 78 @Config(sdk = ConfigConstants.SDK, application = EpgFetcherImplTest.TestApp.class) 79 public class EpgFetcherImplTest { 80 81 /** TestApp for {@link EpgFetcherImplTest} */ 82 public static class TestApp extends TestSingletonApp implements HasAndroidInjector { 83 @Inject DispatchingAndroidInjector<Object> dispatchingAndroidInjector; 84 85 @Override onCreate()86 public void onCreate() { 87 super.onCreate(); 88 DaggerEpgFetcherImplTest_TestAppComponent.builder() 89 .settableFlagsModule(flagsModule) 90 .build() 91 .inject(this); 92 } 93 94 @Override androidInjector()95 public AndroidInjector<Object> androidInjector() { 96 return dispatchingAndroidInjector; 97 } 98 } 99 100 /** Component for {@link EpgFetcherImplTest} */ 101 @Component( 102 modules = { 103 AndroidInjectionModule.class, 104 TestModule.class, 105 EpgContentProvider.Module.class 106 }) 107 interface TestAppComponent extends AndroidInjector<TestApp> {} 108 109 /** Module for {@link EpgFetcherImplTest} */ 110 @Module(includes = {SettableFlagsModule.class}) 111 public static class TestModule {} 112 113 private static final String[] PROGRAM_COLUMNS = { 114 TvContract.Programs.COLUMN_CHANNEL_ID, 115 TvContract.Programs.COLUMN_TITLE, 116 TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS, 117 TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS 118 }; 119 120 private static final String[] CHANNEL_COLUMNS = { 121 TvContract.Channels.COLUMN_DISPLAY_NAME, 122 TvContract.Channels.COLUMN_DISPLAY_NUMBER, 123 TvContract.Channels.COLUMN_NETWORK_AFFILIATION 124 }; 125 126 private FakeClock mFakeClock; 127 private EpgFetcherImpl mEpgFetcher; 128 private ChannelDataManager mChannelDataManager; 129 private FakeEpgReader mEpgReader; 130 private PerformanceMonitor mPerformanceMonitor = new StubPerformanceMonitor(); 131 private ContentResolver mContentResolver; 132 private FakeTvProvider mTvProvider; 133 private EpgContentProvider mEpgProvider; 134 private EpgContentProvider.EpgDatabaseHelper mDatabaseHelper; 135 private TestApp mTestApp; 136 137 @Before setup()138 public void setup() { 139 140 TvFeatures.CLOUD_EPG_FOR_3RD_PARTY.enableForTest(); 141 mTestApp = (TestApp) RuntimeEnvironment.application; 142 Shadows.shadowOf(RuntimeEnvironment.application) 143 .grantPermissions("com.android.providers.tv.permission.ACCESS_ALL_EPG_DATA"); 144 mDatabaseHelper = new EpgContentProvider.EpgDatabaseHelper(RuntimeEnvironment.application); 145 CommonPreferences.initialize(RuntimeEnvironment.application); 146 PostalCodeUtils.setLastPostalCode(RuntimeEnvironment.application, "90210"); 147 EpgFetchHelper.setLastLineupId(RuntimeEnvironment.application, "test90210"); 148 mTvProvider = ContentProviders.register(FakeTvProvider.class, TvContract.AUTHORITY); 149 mEpgProvider = ContentProviders.register(EpgContentProvider.class, EpgContract.AUTHORITY); 150 mEpgProvider.setCallingPackage_("com.google.android.tv"); 151 mFakeClock = FakeClock.createWithCurrentTime(); 152 FakeTvInputManagerHelper fakeTvInputManagerHelper = 153 new FakeTvInputManagerHelper(RuntimeEnvironment.application); 154 mContentResolver = RuntimeEnvironment.application.getContentResolver(); 155 mChannelDataManager = 156 new ChannelDataManager( 157 RuntimeEnvironment.application, 158 fakeTvInputManagerHelper, 159 new RoboExecutorService(), 160 mContentResolver); 161 fakeTvInputManagerHelper.start(); 162 mChannelDataManager.start(); 163 mEpgReader = new FakeEpgReader(mFakeClock); 164 mEpgFetcher = 165 new EpgFetcherImpl( 166 RuntimeEnvironment.application, 167 new EpgInputWhiteList( 168 mTestApp.flagsModule.cloudEpgFlags, 169 mTestApp.flagsModule.legacyFlags), 170 mChannelDataManager, 171 mEpgReader, 172 mPerformanceMonitor, 173 mFakeClock, 174 new DefaultBackendKnobsFlags(), 175 BuildType.NO_JNI_TEST); 176 EpgTestData.DATA_90210.loadData(mFakeClock, mEpgReader); // This also sets fake clock 177 EpgFetchHelper.setLastEpgUpdatedTimestamp( 178 RuntimeEnvironment.application, 179 mFakeClock.currentTimeMillis() - TimeUnit.DAYS.toMillis(1)); 180 } 181 182 @After after()183 public void after() { 184 mChannelDataManager.stop(); 185 TvFeatures.CLOUD_EPG_FOR_3RD_PARTY.resetForTests(); 186 } 187 188 @Test fetchImmediately_nochannels()189 public void fetchImmediately_nochannels() throws ExecutionException, InterruptedException { 190 EpgFetcherImpl.FetchAsyncTask fetcherTask = mEpgFetcher.createFetchTask(null, null); 191 fetcherTask.execute(); 192 193 assertThat(fetcherTask.get()).isEqualTo(EpgFetcherImpl.REASON_NO_BUILT_IN_CHANNELS); 194 List<List<String>> rows = 195 DbTestingUtils.toList( 196 mContentResolver.query( 197 TvContract.Programs.CONTENT_URI, 198 PROGRAM_COLUMNS, 199 null, 200 null, 201 null)); 202 assertThat(rows).isEmpty(); 203 } 204 205 @Test fetchImmediately_testChannel()206 public void fetchImmediately_testChannel() throws ExecutionException, InterruptedException { 207 // The channels must be in the app package. 208 // For this test the package is com.android.tv.data.epg 209 insertTestChannels( 210 "com.android.tv.data.epg/.tuner.TunerTvInputService", EpgTestData.CHANNEL_10); 211 EpgFetcherImpl.FetchAsyncTask fetcherTask = mEpgFetcher.createFetchTask(null, null); 212 fetcherTask.execute(); 213 214 assertThat(fetcherTask.get()).isNull(); 215 List<List<String>> rows = 216 DbTestingUtils.toList( 217 mContentResolver.query( 218 TvContract.Programs.CONTENT_URI, 219 PROGRAM_COLUMNS, 220 null, 221 null, 222 null)); 223 assertThat(rows) 224 .containsExactly( 225 ImmutableList.of("1", "Program 1", "1496358000000", "1496359800000")); 226 } 227 228 @Test fetchImmediately_epgChannel()229 public void fetchImmediately_epgChannel() throws ExecutionException, InterruptedException { 230 mTestApp.flagsModule.cloudEpgFlags.setThirdPartyEpgInput( 231 StringListParam.newBuilder().addElement("com.example/.Input").build()); 232 insertTestChannels("com.example/.Input", EpgTestData.CHANNEL_10, EpgTestData.CHANNEL_11); 233 createTestEpgInput(); 234 EpgFetcherImpl.FetchAsyncTask fetcherTask = mEpgFetcher.createFetchTask(null, null); 235 fetcherTask.execute(); 236 237 assertThat(fetcherTask.get()).isNull(); 238 List<List<String>> rows = 239 DbTestingUtils.toList( 240 mContentResolver.query( 241 TvContract.Programs.CONTENT_URI, 242 PROGRAM_COLUMNS, 243 null, 244 null, 245 null)); 246 assertThat(rows) 247 .containsExactly( 248 ImmutableList.of("1", "Program 1", "1496358000000", "1496359800000"), 249 ImmutableList.of("2", "Program 2", "1496359800000", "1496361600000")); 250 } 251 252 @Test testUpdateNetworkAffiliation()253 public void testUpdateNetworkAffiliation() throws ExecutionException, InterruptedException { 254 if (!TvFeatures.STORE_NETWORK_AFFILIATION.isEnabled(RuntimeEnvironment.application)) { 255 return; 256 } 257 // set network affiliation to null so that it can be updated later 258 Channel channel = 259 new Channel.Builder(EpgTestData.CHANNEL_10).setNetworkAffiliation(null).build(); 260 // The channels must be in the app package. 261 // For this test the package is com.android.tv.data.epg 262 insertTestChannels("com.android.tv.data.epg/.tuner.TunerTvInputService", channel); 263 264 List<List<String>> rows = 265 DbTestingUtils.toList( 266 mContentResolver.query( 267 TvContract.Channels.CONTENT_URI, 268 CHANNEL_COLUMNS, 269 null, 270 null, 271 null)); 272 assertThat(rows).containsExactly(ImmutableList.of("Channel TEN", "10", "null")); 273 EpgFetcherImpl.FetchAsyncTask fetcherTask = mEpgFetcher.createFetchTask(null, null); 274 fetcherTask.execute(); 275 276 assertThat(fetcherTask.get()).isNull(); 277 rows = 278 DbTestingUtils.toList( 279 mContentResolver.query( 280 TvContract.Channels.CONTENT_URI, 281 CHANNEL_COLUMNS, 282 null, 283 null, 284 null)); 285 // network affiliation should be updated 286 assertThat(rows) 287 .containsExactly( 288 ImmutableList.of("Channel TEN", "10", "Channel 10 Network Affiliation")); 289 } 290 insertTestChannels(String inputId, Channel... channels)291 protected void insertTestChannels(String inputId, Channel... channels) { 292 293 for (Channel channel : channels) { 294 ContentValues values = 295 new Channel.Builder(channel).setInputId(inputId).build().toContentValues(); 296 String packageName = inputId.substring(0, inputId.indexOf('/')); 297 mTvProvider.setCallingPackage(packageName); 298 mContentResolver.insert(TvContract.Channels.CONTENT_URI, values); 299 mTvProvider.setCallingPackage("com.android.tv"); 300 } 301 } 302 createTestEpgInput()303 private void createTestEpgInput() { 304 // Use the database helper so we can set the package name. 305 SQLiteDatabase db = mDatabaseHelper.getWritableDatabase(); 306 ContentValues values = new ContentValues(); 307 values.put(EpgContract.EpgInputs.COLUMN_ID, "1"); 308 values.put(EpgContract.EpgInputs.COLUMN_PACKAGE_NAME, "com.example"); 309 values.put(EpgContract.EpgInputs.COLUMN_INPUT_ID, "com.example/.Input"); 310 values.put(EpgContract.EpgInputs.COLUMN_LINEUP_ID, "lineup1"); 311 long rowId = db.insert("epg_input", null, values); 312 assertThat(rowId).isEqualTo(1); 313 } 314 } 315