1 /* 2 * Copyright (C) 2014 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.providers.tv; 18 19 import com.google.android.collect.Sets; 20 21 import android.content.ContentUris; 22 import android.content.ContentValues; 23 import android.content.Intent; 24 import android.content.pm.ProviderInfo; 25 import android.database.Cursor; 26 import android.media.tv.TvContract; 27 import android.media.tv.TvContract.Channels; 28 import android.media.tv.TvContract.Programs; 29 import android.media.tv.TvContract.WatchedPrograms; 30 import android.net.Uri; 31 import android.os.Bundle; 32 import android.os.SystemClock; 33 import android.provider.Settings; 34 import android.test.suitebuilder.annotation.Suppress; 35 import android.test.mock.MockContentProvider; 36 import android.test.mock.MockContentResolver; 37 import android.test.ServiceTestCase; 38 39 import java.util.ArrayList; 40 import java.util.Arrays; 41 import java.util.Collection; 42 import java.util.HashSet; 43 import java.util.Objects; 44 import java.util.Set; 45 46 public class EpgDataCleanupServiceTests extends ServiceTestCase<EpgDataCleanupService> { 47 private static final String FAKE_INPUT_ID = "EpgDataCleanupServiceTests"; 48 49 private MockContentResolver mResolver; 50 private TvProviderForTesting mProvider; 51 EpgDataCleanupServiceTests()52 public EpgDataCleanupServiceTests() { 53 super(EpgDataCleanupService.class); 54 } 55 56 @Override setUp()57 protected void setUp() throws Exception { 58 super.setUp(); 59 60 mResolver = new MockContentResolver(); 61 // DateUtils tries to access Settings provider to get date format string. 62 mResolver.addProvider(Settings.AUTHORITY, new MockContentProvider() { 63 @Override 64 public Bundle call(String method, String request, Bundle args) { 65 return new Bundle(); 66 } 67 }); 68 69 mProvider = new TvProviderForTesting(); 70 mResolver.addProvider(TvContract.AUTHORITY, mProvider); 71 72 setContext(new MockTvProviderContext(mResolver, getSystemContext())); 73 74 final ProviderInfo info = new ProviderInfo(); 75 info.authority = TvContract.AUTHORITY; 76 mProvider.attachInfoForTesting(getContext(), info); 77 78 Utils.clearTvProvider(mResolver); 79 startService(new Intent(getContext(), EpgDataCleanupService.class)); 80 } 81 82 @Override tearDown()83 protected void tearDown() throws Exception { 84 Utils.clearTvProvider(mResolver); 85 mProvider.shutdown(); 86 super.tearDown(); 87 } 88 89 private static class Program { 90 long id; 91 final long startTime; 92 final long endTime; 93 Program(long startTime, long endTime)94 Program(long startTime, long endTime) { 95 this(-1, startTime, endTime); 96 } 97 Program(long id, long startTime, long endTime)98 Program(long id, long startTime, long endTime) { 99 this.id = id; 100 this.startTime = startTime; 101 this.endTime = endTime; 102 } 103 104 @Override equals(Object obj)105 public boolean equals(Object obj) { 106 if (!(obj instanceof Program)) { 107 return false; 108 } 109 Program that = (Program) obj; 110 return Objects.equals(id, that.id) 111 && Objects.equals(startTime, that.startTime) 112 && Objects.equals(endTime, that.endTime); 113 } 114 115 @Override hashCode()116 public int hashCode() { 117 return Objects.hash(id, startTime, endTime); 118 } 119 120 @Override toString()121 public String toString() { 122 return "Program(id=" + id + ",start=" + startTime + ",end=" + endTime + ")"; 123 } 124 } 125 insertChannel()126 private long insertChannel() { 127 ContentValues values = new ContentValues(); 128 values.put(Channels.COLUMN_INPUT_ID, FAKE_INPUT_ID); 129 Uri uri = mResolver.insert(Channels.CONTENT_URI, values); 130 assertNotNull(uri); 131 return ContentUris.parseId(uri); 132 } 133 insertPrograms(Program... programs)134 private void insertPrograms(Program... programs) { 135 insertPrograms(Arrays.asList(programs)); 136 } 137 insertPrograms(Collection<Program> programs)138 private void insertPrograms(Collection<Program> programs) { 139 long channelId = insertChannel(); 140 141 ContentValues values = new ContentValues(); 142 values.put(Programs.COLUMN_CHANNEL_ID, channelId); 143 for (Program program : programs) { 144 values.put(Programs.COLUMN_START_TIME_UTC_MILLIS, program.startTime); 145 values.put(Programs.COLUMN_END_TIME_UTC_MILLIS, program.endTime); 146 Uri uri = mResolver.insert(Programs.CONTENT_URI, values); 147 assertNotNull(uri); 148 program.id = ContentUris.parseId(uri); 149 } 150 } 151 queryPrograms()152 private Set<Program> queryPrograms() { 153 String[] projection = new String[] { 154 Programs._ID, 155 Programs.COLUMN_START_TIME_UTC_MILLIS, 156 Programs.COLUMN_END_TIME_UTC_MILLIS, 157 }; 158 159 Cursor cursor = mResolver.query(Programs.CONTENT_URI, projection, null, null, null); 160 assertNotNull(cursor); 161 try { 162 Set<Program> programs = Sets.newHashSet(); 163 while (cursor.moveToNext()) { 164 programs.add(new Program(cursor.getLong(0), cursor.getLong(1), cursor.getLong(2))); 165 } 166 return programs; 167 } finally { 168 cursor.close(); 169 } 170 } 171 insertWatchedPrograms(Program... programs)172 private void insertWatchedPrograms(Program... programs) { 173 insertWatchedPrograms(Arrays.asList(programs)); 174 } 175 insertWatchedPrograms(Collection<Program> programs)176 private void insertWatchedPrograms(Collection<Program> programs) { 177 long channelId = insertChannel(); 178 179 ContentValues values = new ContentValues(); 180 values.put(WatchedPrograms.COLUMN_PACKAGE_NAME, getContext().getPackageName()); 181 values.put(WatchedPrograms.COLUMN_CHANNEL_ID, channelId); 182 for (Program program : programs) { 183 values.put(WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS, program.startTime); 184 values.put(WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS, program.endTime); 185 Uri uri = mProvider.insertWatchedProgramSync(values); 186 assertNotNull(uri); 187 program.id = ContentUris.parseId(uri); 188 } 189 } 190 queryWatchedPrograms()191 private Set<Program> queryWatchedPrograms() { 192 String[] projection = new String[] { 193 WatchedPrograms._ID, 194 WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS, 195 WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS, 196 }; 197 198 Cursor cursor = mResolver.query(WatchedPrograms.CONTENT_URI, projection, null, null, null); 199 assertNotNull(cursor); 200 try { 201 Set<Program> programs = Sets.newHashSet(); 202 while (cursor.moveToNext()) { 203 programs.add(new Program(cursor.getLong(0), cursor.getLong(1), cursor.getLong(2))); 204 } 205 return programs; 206 } finally { 207 cursor.close(); 208 } 209 } 210 211 @Override testServiceTestCaseSetUpProperly()212 public void testServiceTestCaseSetUpProperly() throws Exception { 213 assertNotNull(getService()); 214 } 215 testClearOldPrograms()216 public void testClearOldPrograms() { 217 Program program = new Program(1, 2); 218 insertPrograms(program); 219 220 getService().clearOldPrograms(2); 221 assertEquals("Program should NOT be deleted if it ended at given time.", 222 Sets.newHashSet(program), queryPrograms()); 223 224 getService().clearOldPrograms(3); 225 assertTrue("Program should be deleted if it ended before given time.", 226 queryPrograms().isEmpty()); 227 228 ArrayList<Program> programs = new ArrayList<Program>(); 229 for (int i = 0; i < 10; i++) { 230 programs.add(new Program(999 + i, 1000 + i)); 231 } 232 insertPrograms(programs); 233 234 getService().clearOldPrograms(1005); 235 assertEquals("Program should be deleted if and only if it ended before given time.", 236 new HashSet<Program>(programs.subList(5, 10)), queryPrograms()); 237 } 238 239 // Disable temporarily since it's not trivial to fix due to asynchronous implementation of 240 // watch history management. testClearOldWatchedPrograms()241 public void testClearOldWatchedPrograms() { 242 Program program = new Program(1, 2); 243 insertWatchedPrograms(program); 244 245 getService().clearOldWatchHistory(1); 246 assertEquals("Watch history should NOT be deleted if watch started at given time.", 247 Sets.newHashSet(program), queryWatchedPrograms()); 248 249 getService().clearOldWatchHistory(2); 250 assertTrue("Watch history shuold be deleted if watch started before given time.", 251 queryWatchedPrograms().isEmpty()); 252 253 ArrayList<Program> programs = new ArrayList<Program>(); 254 for (int i = 0; i < 10; i++) { 255 programs.add(new Program(1000 + i, 1001 + i)); 256 } 257 insertWatchedPrograms(programs); 258 259 getService().clearOldWatchHistory(1005); 260 assertEquals("Watch history should be deleted if and only if it started before given time.", 261 new HashSet<Program>(programs.subList(5, 10)), queryWatchedPrograms()); 262 } 263 264 // Disable temporarily since it's not trivial to fix due to asynchronous implementation of 265 // watch history management. testClearOverflowWatchHistory()266 public void testClearOverflowWatchHistory() { 267 ArrayList<Program> programs = new ArrayList<Program>(); 268 for (int i = 0; i < 10; i++) { 269 programs.add(new Program(1000 + i, 1001 + i)); 270 } 271 insertWatchedPrograms(programs); 272 273 getService().clearOverflowWatchHistory(5); 274 assertEquals("Watch history should be deleted in watch start time order.", 275 new HashSet<Program>(programs.subList(5, 10)), queryWatchedPrograms()); 276 277 getService().clearOverflowWatchHistory(0); 278 assertTrue("All history should be deleted.", queryWatchedPrograms().isEmpty()); 279 } 280 } 281