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 package android.support.media.tv; 17 18 import android.content.ContentResolver; 19 import android.content.ContentUris; 20 import android.content.ContentValues; 21 import android.database.Cursor; 22 import android.database.MatrixCursor; 23 import android.media.tv.TvContentRating; 24 import android.net.Uri; 25 import android.os.Build; 26 import android.support.media.tv.TvContractCompat.Channels; 27 import android.support.media.tv.TvContractCompat.Programs; 28 import android.support.test.InstrumentationRegistry; 29 import android.support.test.filters.SdkSuppress; 30 import android.support.test.filters.SmallTest; 31 32 import junit.framework.TestCase; 33 34 import org.junit.Test; 35 36 import java.util.Arrays; 37 import java.util.Objects; 38 39 /** 40 * Tests that programs can be created using the Builder pattern and correctly obtain 41 * values from them. 42 */ 43 @SmallTest 44 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP) 45 public class ProgramTest extends TestCase { 46 @Override 47 protected void tearDown() { 48 if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) { 49 return; 50 } 51 ContentResolver resolver = InstrumentationRegistry.getContext().getContentResolver(); 52 resolver.delete(Channels.CONTENT_URI, null, null); 53 } 54 55 @Test 56 public void testEmptyProgram() { 57 Program emptyProgram = new Program.Builder() 58 .build(); 59 ContentValues contentValues = emptyProgram.toContentValues(); 60 compareProgram(emptyProgram, 61 Program.fromCursor(getProgramCursor(Program.PROJECTION, contentValues)), true); 62 } 63 64 @Test 65 public void testSampleProgram() { 66 Program sampleProgram = new Program.Builder() 67 .setPackageName("My package") 68 .setTitle("Program Title") 69 .setDescription("This is a sample program") 70 .setEpisodeNumber(5) 71 .setSeasonNumber("The Final Season", 7) 72 .setThumbnailUri(Uri.parse("http://www.example.com/programs/poster.png")) 73 .setChannelId(3) 74 .setStartTimeUtcMillis(0) 75 .setEndTimeUtcMillis(1000) 76 .build(); 77 ContentValues contentValues = sampleProgram.toContentValues(); 78 compareProgram(sampleProgram, 79 Program.fromCursor(getProgramCursor(Program.PROJECTION, contentValues)), true); 80 81 Program clonedSampleProgram = new Program.Builder(sampleProgram).build(); 82 compareProgram(sampleProgram, clonedSampleProgram, true); 83 } 84 85 @Test 86 public void testFullyPopulatedProgram() { 87 Program fullyPopulatedProgram = createFullyPopulatedProgram(3); 88 89 ContentValues contentValues = fullyPopulatedProgram.toContentValues(); 90 compareProgram(fullyPopulatedProgram, 91 Program.fromCursor(getProgramCursor(Program.PROJECTION, contentValues)), true); 92 93 Program clonedFullyPopulatedProgram = new Program.Builder(fullyPopulatedProgram).build(); 94 compareProgram(fullyPopulatedProgram, clonedFullyPopulatedProgram, true); 95 } 96 97 @Test 98 public void testChannelWithSystemContentProvider() { 99 if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) { 100 return; 101 } 102 Channel channel = new Channel.Builder() 103 .setInputId("TestInputService") 104 .setType(TvContractCompat.Channels.TYPE_OTHER) 105 .build(); 106 ContentResolver resolver = InstrumentationRegistry.getContext().getContentResolver(); 107 Uri channelUri = resolver.insert(Channels.CONTENT_URI, channel.toContentValues()); 108 assertNotNull(channelUri); 109 110 Program fullyPopulatedProgram = 111 createFullyPopulatedProgram(ContentUris.parseId(channelUri)); 112 Uri programUri = resolver.insert(Programs.CONTENT_URI, 113 fullyPopulatedProgram.toContentValues()); 114 115 Program programFromSystemDb = loadProgramFromContentProvider(resolver, programUri); 116 compareProgram(fullyPopulatedProgram, programFromSystemDb, false); 117 } 118 119 @Test 120 public void testProgramUpdateWithContentProvider() { 121 if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) { 122 return; 123 } 124 Channel channel = new Channel.Builder() 125 .setInputId("TestInputService") 126 .setType(TvContractCompat.Channels.TYPE_OTHER) 127 .build(); 128 ContentResolver resolver = InstrumentationRegistry.getContext().getContentResolver(); 129 Uri channelUri = resolver.insert(Channels.CONTENT_URI, channel.toContentValues()); 130 assertNotNull(channelUri); 131 132 Program fullyPopulatedProgram = 133 createFullyPopulatedProgram(ContentUris.parseId(channelUri)); 134 Uri programUri = resolver.insert(Programs.CONTENT_URI, 135 fullyPopulatedProgram.toContentValues()); 136 137 Program programFromSystemDb = loadProgramFromContentProvider(resolver, programUri); 138 compareProgram(fullyPopulatedProgram, programFromSystemDb, false); 139 140 // Update a field from a fully loaded program. 141 Program updatedProgram = new Program.Builder(programFromSystemDb) 142 .setDescription("description1").build(); 143 assertEquals(1, resolver.update( 144 programUri, updatedProgram.toContentValues(), null, null)); 145 programFromSystemDb = loadProgramFromContentProvider(resolver, programUri); 146 compareProgram(updatedProgram, programFromSystemDb, false); 147 148 // Update a field with null from a fully loaded program. 149 updatedProgram = new Program.Builder(updatedProgram) 150 .setLongDescription(null).build(); 151 assertEquals(1, resolver.update( 152 programUri, updatedProgram.toContentValues(), null, null)); 153 programFromSystemDb = loadProgramFromContentProvider(resolver, programUri); 154 compareProgram(updatedProgram, programFromSystemDb, false); 155 156 // Update a field without referencing fully loaded program. 157 ContentValues values = new Program.Builder().setDescription("description2").build() 158 .toContentValues(); 159 assertEquals(1, values.size()); 160 assertEquals(1, resolver.update(programUri, values, null, null)); 161 programFromSystemDb = loadProgramFromContentProvider(resolver, programUri); 162 Program expectedProgram = new Program.Builder(programFromSystemDb) 163 .setDescription("description2").build(); 164 compareProgram(expectedProgram, programFromSystemDb, false); 165 } 166 167 @Test 168 public void testProgramEquals() { 169 assertEquals(createFullyPopulatedProgram(1), createFullyPopulatedProgram(1)); 170 } 171 172 private static Program loadProgramFromContentProvider( 173 ContentResolver resolver, Uri programUri) { 174 try (Cursor cursor = resolver.query(programUri, null, null, null, null)) { 175 assertNotNull(cursor); 176 assertEquals(1, cursor.getCount()); 177 cursor.moveToNext(); 178 return Program.fromCursor(cursor); 179 } 180 } 181 182 private static Program createFullyPopulatedProgram(long channelId) { 183 return new Program.Builder() 184 .setSearchable(false) 185 .setThumbnailUri(Uri.parse("http://example.com/thumbnail.png")) 186 .setAudioLanguages(new String [] {"eng", "kor"}) 187 .setCanonicalGenres(new String[] {TvContractCompat.Programs.Genres.MOVIES}) 188 .setContentRatings(new TvContentRating[] { 189 TvContentRating.createRating("com.android.tv", "US_TV", "US_TV_Y7")}) 190 .setDescription("This is a sample program") 191 .setEpisodeNumber("Pilot", 0) 192 .setEpisodeTitle("Hello World") 193 .setLongDescription("This is a longer description than the previous description") 194 .setPosterArtUri(Uri.parse("http://example.com/poster.png")) 195 .setSeasonNumber("The Final Season", 7) 196 .setSeasonTitle("The Final Season") 197 .setTitle("Google") 198 .setVideoHeight(1080) 199 .setVideoWidth(1920) 200 .setInternalProviderFlag1(0x4) 201 .setInternalProviderFlag2(0x3) 202 .setInternalProviderFlag3(0x2) 203 .setInternalProviderFlag4(0x1) 204 .setReviewRatingStyle(Programs.REVIEW_RATING_STYLE_PERCENTAGE) 205 .setReviewRating("83.9") 206 .setChannelId(channelId) 207 .setStartTimeUtcMillis(0) 208 .setEndTimeUtcMillis(1000) 209 .setBroadcastGenres(new String[] {"Music", "Family"}) 210 .setRecordingProhibited(false) 211 .build(); 212 } 213 214 private static void compareProgram(Program programA, Program programB, 215 boolean includeIdAndProtectedFields) { 216 assertTrue(Arrays.equals(programA.getAudioLanguages(), programB.getAudioLanguages())); 217 assertTrue(Arrays.deepEquals(programA.getBroadcastGenres(), programB.getBroadcastGenres())); 218 assertTrue(Arrays.deepEquals(programA.getCanonicalGenres(), programB.getCanonicalGenres())); 219 assertEquals(programA.getChannelId(), programB.getChannelId()); 220 assertTrue(Arrays.deepEquals(programA.getContentRatings(), programB.getContentRatings())); 221 assertEquals(programA.getDescription(), programB.getDescription()); 222 assertEquals(programA.getEndTimeUtcMillis(), programB.getEndTimeUtcMillis()); 223 assertEquals(programA.getEpisodeNumber(), programB.getEpisodeNumber()); 224 assertEquals(programA.getEpisodeTitle(), programB.getEpisodeTitle()); 225 assertEquals(programA.getLongDescription(), programB.getLongDescription()); 226 assertEquals(programA.getPosterArtUri(), programB.getPosterArtUri()); 227 assertEquals(programA.getSeasonNumber(), programB.getSeasonNumber()); 228 assertEquals(programA.getStartTimeUtcMillis(), programB.getStartTimeUtcMillis()); 229 assertEquals(programA.getThumbnailUri(), programB.getThumbnailUri()); 230 assertEquals(programA.getTitle(), programB.getTitle()); 231 assertEquals(programA.getVideoHeight(), programB.getVideoHeight()); 232 assertEquals(programA.getVideoWidth(), programB.getVideoWidth()); 233 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 234 assertEquals(programA.isSearchable(), programB.isSearchable()); 235 assertEquals(programA.getInternalProviderFlag1(), programB.getInternalProviderFlag1()); 236 assertEquals(programA.getInternalProviderFlag2(), programB.getInternalProviderFlag2()); 237 assertEquals(programA.getInternalProviderFlag3(), programB.getInternalProviderFlag3()); 238 assertEquals(programA.getInternalProviderFlag4(), programB.getInternalProviderFlag4()); 239 } 240 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 241 assertTrue(Objects.equals(programA.getSeasonTitle(), programB.getSeasonTitle())); 242 assertTrue(Objects.equals(programA.isRecordingProhibited(), 243 programB.isRecordingProhibited())); 244 } 245 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 246 assertEquals(programA.getReviewRatingStyle(), programB.getReviewRatingStyle()); 247 assertEquals(programA.getReviewRating(), programB.getReviewRating()); 248 } 249 if (includeIdAndProtectedFields) { 250 // Skip row ID since the one from system DB has the valid ID while the other does not. 251 assertEquals(programA.getId(), programB.getId()); 252 assertEquals(programA.getPackageName(), programB.getPackageName()); 253 assertEquals(programA.toContentValues(), programB.toContentValues()); 254 } 255 } 256 257 private static MatrixCursor getProgramCursor(String[] projection, ContentValues contentValues) { 258 MatrixCursor cursor = new MatrixCursor(projection); 259 MatrixCursor.RowBuilder builder = cursor.newRow(); 260 for (String row : projection) { 261 if (row != null) { 262 builder.add(row, contentValues.get(row)); 263 } 264 } 265 cursor.moveToFirst(); 266 return cursor; 267 } 268 } 269