• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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