1/*
2 * Copyright 2018 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 */
16package androidx.tvprovider.media.tv;
17
18import static org.junit.Assert.assertEquals;
19import static org.junit.Assert.assertFalse;
20import static org.junit.Assert.assertNotNull;
21import static org.junit.Assert.assertNull;
22import static org.junit.Assert.assertTrue;
23
24import android.content.Context;
25import android.content.Intent;
26import android.graphics.Bitmap;
27import android.graphics.BitmapFactory;
28import android.media.tv.TvContentRating;
29import android.net.Uri;
30import android.os.Build;
31import android.support.test.InstrumentationRegistry;
32import android.support.test.filters.SdkSuppress;
33import android.support.test.filters.SmallTest;
34
35import org.junit.After;
36import org.junit.Before;
37import org.junit.Rule;
38import org.junit.Test;
39import org.junit.rules.ExpectedException;
40import org.junit.runner.RunWith;
41import org.junit.runners.JUnit4;
42
43import java.io.IOException;
44import java.util.Arrays;
45import java.util.List;
46import java.util.Objects;
47
48/**
49 * Test that {@link PreviewChannelHelper} can perform CRUD operations on
50 * {@link PreviewChannel PreviewChannels} and {@link PreviewProgram PreviewPrograms} correctly.
51 * All of the following tests involve the system content provider.
52 */
53@SmallTest
54@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
55@RunWith(JUnit4.class)
56public class PreviewChannelHelperTest {
57
58
59    private Context mContext;
60
61    @Rule
62    public ExpectedException thrown = ExpectedException.none();
63
64    /**
65     * taken from {@link PreviewProgram}
66     */
67    private static PreviewProgram.Builder createFullyPopulatedPreviewProgram(long channelId) {
68        return new PreviewProgram.Builder()
69                .setTitle("Google")
70                .setInternalProviderId("ID-4321")
71                .setChannelId(channelId)
72                .setWeight(100)
73                .setPreviewVideoUri(Uri.parse("http://example.com/preview-video.mpg"))
74                .setLastPlaybackPositionMillis(0)
75                .setDurationMillis(60 * 1000)
76                .setIntentUri(Uri.parse(new Intent(Intent.ACTION_VIEW).toUri(
77                        Intent.URI_INTENT_SCHEME)))
78                .setTransient(false)
79                .setType(TvContractCompat.PreviewPrograms.TYPE_MOVIE)
80                .setPosterArtAspectRatio(TvContractCompat.PreviewPrograms.ASPECT_RATIO_2_3)
81                .setThumbnailAspectRatio(TvContractCompat.PreviewPrograms.ASPECT_RATIO_16_9)
82                .setLogoUri(Uri.parse("http://example.com/program-logo.mpg"))
83                .setAvailability(TvContractCompat.PreviewPrograms.AVAILABILITY_AVAILABLE)
84                .setStartingPrice("12.99 USD")
85                .setOfferPrice("4.99 USD")
86                .setReleaseDate("1997")
87                .setItemCount(3)
88                .setLive(false)
89                .setInteractionType(TvContractCompat.PreviewPrograms.INTERACTION_TYPE_LIKES)
90                .setInteractionCount(10200)
91                .setAuthor("author_name")
92                .setReviewRatingStyle(TvContractCompat.PreviewPrograms.REVIEW_RATING_STYLE_STARS)
93                .setReviewRating("4.5")
94                .setSearchable(false)
95                .setThumbnailUri(Uri.parse("http://example.com/thumbnail.png"))
96                .setAudioLanguages(new String[]{"eng", "kor"})
97                .setCanonicalGenres(new String[]{TvContractCompat.Programs.Genres.MOVIES})
98                .setContentRatings(new TvContentRating[]{
99                        TvContentRating.createRating("com.android.tv", "US_TV", "US_TV_Y7")})
100                .setDescription("This is a sample program")
101                .setEpisodeNumber("Pilot", 0)
102                .setEpisodeTitle("Hello World")
103                .setLongDescription("This is a longer description than the previous description")
104                .setPosterArtUri(Uri.parse("http://example.com/poster.png"))
105                .setSeasonNumber("The Final Season", 7)
106                .setSeasonTitle("The Final Season")
107                .setVideoHeight(1080)
108                .setVideoWidth(1920)
109                .setInternalProviderFlag1(0x4)
110                .setInternalProviderFlag2(0x3)
111                .setInternalProviderFlag3(0x2)
112                .setInternalProviderFlag4(0x1)
113                .setBrowsable(true)
114                .setContentId("CID-8642");
115    }
116
117    private static void compareProgram(PreviewProgram programA, PreviewProgram programB) {
118        assertTrue(Arrays.equals(programA.getAudioLanguages(), programB.getAudioLanguages()));
119        assertTrue(Arrays.deepEquals(programA.getCanonicalGenres(), programB.getCanonicalGenres()));
120        assertEquals(programA.getChannelId(), programB.getChannelId());
121        assertTrue(Arrays.deepEquals(programA.getContentRatings(), programB.getContentRatings()));
122        assertEquals(programA.getDescription(), programB.getDescription());
123        assertEquals(programA.getEpisodeNumber(), programB.getEpisodeNumber());
124        assertEquals(programA.getEpisodeTitle(), programB.getEpisodeTitle());
125        assertEquals(programA.getLongDescription(), programB.getLongDescription());
126        assertEquals(programA.getPosterArtUri(), programB.getPosterArtUri());
127        assertEquals(programA.getSeasonNumber(), programB.getSeasonNumber());
128        assertEquals(programA.getThumbnailUri(), programB.getThumbnailUri());
129        assertEquals(programA.getTitle(), programB.getTitle());
130        assertEquals(programA.getVideoHeight(), programB.getVideoHeight());
131        assertEquals(programA.getVideoWidth(), programB.getVideoWidth());
132        assertEquals(programA.isSearchable(), programB.isSearchable());
133        assertEquals(programA.getInternalProviderFlag1(), programB.getInternalProviderFlag1());
134        assertEquals(programA.getInternalProviderFlag2(), programB.getInternalProviderFlag2());
135        assertEquals(programA.getInternalProviderFlag3(), programB.getInternalProviderFlag3());
136        assertEquals(programA.getInternalProviderFlag4(), programB.getInternalProviderFlag4());
137        assertTrue(Objects.equals(programA.getSeasonTitle(), programB.getSeasonTitle()));
138        assertEquals(programA.getInternalProviderId(), programB.getInternalProviderId());
139        assertEquals(programA.getPreviewVideoUri(), programB.getPreviewVideoUri());
140        assertEquals(programA.getLastPlaybackPositionMillis(),
141                programB.getLastPlaybackPositionMillis());
142        assertEquals(programA.getDurationMillis(), programB.getDurationMillis());
143        assertEquals(programA.getIntentUri(), programB.getIntentUri());
144        assertEquals(programA.getWeight(), programB.getWeight());
145        assertEquals(programA.isTransient(), programB.isTransient());
146        assertEquals(programA.getType(), programB.getType());
147        assertEquals(programA.getPosterArtAspectRatio(), programB.getPosterArtAspectRatio());
148        assertEquals(programA.getThumbnailAspectRatio(), programB.getThumbnailAspectRatio());
149        assertEquals(programA.getLogoUri(), programB.getLogoUri());
150        assertEquals(programA.getAvailability(), programB.getAvailability());
151        assertEquals(programA.getStartingPrice(), programB.getStartingPrice());
152        assertEquals(programA.getOfferPrice(), programB.getOfferPrice());
153        assertEquals(programA.getReleaseDate(), programB.getReleaseDate());
154        assertEquals(programA.getItemCount(), programB.getItemCount());
155        assertEquals(programA.isLive(), programB.isLive());
156        assertEquals(programA.getInteractionType(), programB.getInteractionType());
157        assertEquals(programA.getInteractionCount(), programB.getInteractionCount());
158        assertEquals(programA.getAuthor(), programB.getAuthor());
159        assertEquals(programA.getReviewRatingStyle(), programB.getReviewRatingStyle());
160        assertEquals(programA.getReviewRating(), programB.getReviewRating());
161        assertEquals(programA.getContentId(), programB.getContentId());
162    }
163
164    private static WatchNextProgram.Builder createFullyPopulatedWatchNextProgram() {
165        return new WatchNextProgram.Builder()
166                .setTitle("Google")
167                .setInternalProviderId("ID-4321")
168                .setPreviewVideoUri(Uri.parse("http://example.com/preview-video.mpg"))
169                .setLastPlaybackPositionMillis(0)
170                .setDurationMillis(60 * 1000)
171                .setIntentUri(Uri.parse(new Intent(Intent.ACTION_VIEW).toUri(
172                        Intent.URI_INTENT_SCHEME)))
173                .setTransient(false)
174                .setType(TvContractCompat.WatchNextPrograms.TYPE_MOVIE)
175                .setWatchNextType(TvContractCompat.WatchNextPrograms.WATCH_NEXT_TYPE_CONTINUE)
176                .setPosterArtAspectRatio(TvContractCompat.WatchNextPrograms.ASPECT_RATIO_2_3)
177                .setThumbnailAspectRatio(TvContractCompat.WatchNextPrograms.ASPECT_RATIO_16_9)
178                .setLogoUri(Uri.parse("http://example.com/program-logo.mpg"))
179                .setAvailability(TvContractCompat.WatchNextPrograms.AVAILABILITY_AVAILABLE)
180                .setStartingPrice("12.99 USD")
181                .setOfferPrice("4.99 USD")
182                .setReleaseDate("1997")
183                .setItemCount(3)
184                .setLive(false)
185                .setInteractionType(TvContractCompat.WatchNextPrograms.INTERACTION_TYPE_LIKES)
186                .setInteractionCount(10200)
187                .setAuthor("author_name")
188                .setReviewRatingStyle(TvContractCompat.WatchNextPrograms.REVIEW_RATING_STYLE_STARS)
189                .setReviewRating("4.5")
190                .setSearchable(false)
191                .setThumbnailUri(Uri.parse("http://example.com/thumbnail.png"))
192                .setAudioLanguages(new String[]{"eng", "kor"})
193                .setCanonicalGenres(new String[]{TvContractCompat.Programs.Genres.MOVIES})
194                .setContentRatings(new TvContentRating[]{
195                        TvContentRating.createRating("com.android.tv", "US_TV", "US_TV_Y7")})
196                .setDescription("This is a sample program")
197                .setEpisodeNumber("Pilot", 0)
198                .setEpisodeTitle("Hello World")
199                .setLongDescription("This is a longer description than the previous description")
200                .setPosterArtUri(Uri.parse("http://example.com/poster.png"))
201                .setSeasonNumber("The Final Season", 7)
202                .setSeasonTitle("The Final Season")
203                .setVideoHeight(1080)
204                .setVideoWidth(1920)
205                .setInternalProviderFlag1(0x4)
206                .setInternalProviderFlag2(0x3)
207                .setInternalProviderFlag3(0x2)
208                .setInternalProviderFlag4(0x1)
209                .setBrowsable(true)
210                .setContentId("CID-8442");
211    }
212
213    private static void compareProgram(WatchNextProgram programA, WatchNextProgram programB) {
214        assertTrue(Arrays.equals(programA.getAudioLanguages(), programB.getAudioLanguages()));
215        assertTrue(Arrays.deepEquals(programA.getCanonicalGenres(), programB.getCanonicalGenres()));
216        assertTrue(Arrays.deepEquals(programA.getContentRatings(), programB.getContentRatings()));
217        assertEquals(programA.getDescription(), programB.getDescription());
218        assertEquals(programA.getEpisodeNumber(), programB.getEpisodeNumber());
219        assertEquals(programA.getEpisodeTitle(), programB.getEpisodeTitle());
220        assertEquals(programA.getLongDescription(), programB.getLongDescription());
221        assertEquals(programA.getPosterArtUri(), programB.getPosterArtUri());
222        assertEquals(programA.getSeasonNumber(), programB.getSeasonNumber());
223        assertEquals(programA.getThumbnailUri(), programB.getThumbnailUri());
224        assertEquals(programA.getTitle(), programB.getTitle());
225        assertEquals(programA.getVideoHeight(), programB.getVideoHeight());
226        assertEquals(programA.getVideoWidth(), programB.getVideoWidth());
227        assertEquals(programA.isSearchable(), programB.isSearchable());
228        assertEquals(programA.getInternalProviderFlag1(), programB.getInternalProviderFlag1());
229        assertEquals(programA.getInternalProviderFlag2(), programB.getInternalProviderFlag2());
230        assertEquals(programA.getInternalProviderFlag3(), programB.getInternalProviderFlag3());
231        assertEquals(programA.getInternalProviderFlag4(), programB.getInternalProviderFlag4());
232        assertTrue(Objects.equals(programA.getSeasonTitle(), programB.getSeasonTitle()));
233        assertEquals(programA.getInternalProviderId(), programB.getInternalProviderId());
234        assertEquals(programA.getPreviewVideoUri(), programB.getPreviewVideoUri());
235        assertEquals(programA.getLastPlaybackPositionMillis(),
236                programB.getLastPlaybackPositionMillis());
237        assertEquals(programA.getDurationMillis(), programB.getDurationMillis());
238        assertEquals(programA.getIntentUri(), programB.getIntentUri());
239        assertEquals(programA.isTransient(), programB.isTransient());
240        assertEquals(programA.getType(), programB.getType());
241        assertEquals(programA.getWatchNextType(), programB.getWatchNextType());
242        assertEquals(programA.getPosterArtAspectRatio(), programB.getPosterArtAspectRatio());
243        assertEquals(programA.getThumbnailAspectRatio(), programB.getThumbnailAspectRatio());
244        assertEquals(programA.getLogoUri(), programB.getLogoUri());
245        assertEquals(programA.getAvailability(), programB.getAvailability());
246        assertEquals(programA.getStartingPrice(), programB.getStartingPrice());
247        assertEquals(programA.getOfferPrice(), programB.getOfferPrice());
248        assertEquals(programA.getReleaseDate(), programB.getReleaseDate());
249        assertEquals(programA.getItemCount(), programB.getItemCount());
250        assertEquals(programA.isLive(), programB.isLive());
251        assertEquals(programA.getInteractionType(), programB.getInteractionType());
252        assertEquals(programA.getInteractionCount(), programB.getInteractionCount());
253        assertEquals(programA.getAuthor(), programB.getAuthor());
254        assertEquals(programA.getReviewRatingStyle(), programB.getReviewRatingStyle());
255        assertEquals(programA.getReviewRating(), programB.getReviewRating());
256        assertEquals(programA.getContentId(), programB.getContentId());
257    }
258
259    @Before
260    public void setUp() throws Exception {
261        mContext = InstrumentationRegistry.getContext();
262
263    }
264
265    @After
266    public void tearDown() {
267        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
268            return;
269        }
270        mContext.getContentResolver().delete(
271                TvContractCompat.Channels.CONTENT_URI, null, null);
272        mContext = null;
273    }
274
275    /**
276     * Test CR of CRUD
277     * Test that the PreviewChannelHelper can correctly create and read preview channels.
278     */
279    @Test
280    public void testPreviewChannelCreation() throws IOException {
281        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
282            return;
283        }
284        PreviewChannelHelper helper = new PreviewChannelHelper(mContext);
285        PreviewChannel.Builder builder = createFullyPopulatedPreviewChannel();
286        long channelId = helper.publishDefaultChannel(builder.build());
287        PreviewChannel channelFromTvProvider = getPreviewChannel(helper, channelId);
288        assertTrue(channelsEqual(builder.build(), channelFromTvProvider));
289    }
290
291    @Test
292    public void testLogoRequiredForChannelCreation() throws IOException {
293        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
294            return;
295        }
296        PreviewChannelHelper helper = new PreviewChannelHelper(mContext);
297        PreviewChannel.Builder builder = createFullyPopulatedPreviewChannel();
298        builder.setLogo(Uri.parse("bogus"));
299        thrown.expect(IOException.class);
300        helper.publishDefaultChannel(builder.build());
301        List<PreviewChannel> channels = helper.getAllChannels();
302        assertEquals(0, channels.size());
303    }
304
305    /**
306     * Test CR of CRUD
307     * Test that the PreviewChannelHelper can correctly create and read preview channels, when
308     * internalProviderId is null.
309     */
310    @Test
311    public void testPreviewChannelCreationWithNullProviderId() throws IOException {
312        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
313            return;
314        }
315        PreviewChannel.Builder builder = createFullyPopulatedPreviewChannel();
316        builder.setInternalProviderId(null);
317        PreviewChannelHelper helper = new PreviewChannelHelper(mContext);
318        long channelId = helper.publishChannel(builder.build());
319        PreviewChannel channelFromTvProvider = getPreviewChannel(helper, channelId);
320        assertTrue(channelsEqual(builder.build(), channelFromTvProvider));
321    }
322
323    /**
324     * All this method is actually doing is
325     * <pre>
326     *
327     *     PreviewChannel channelFromTvProvider = helper.getPreviewChannel(channelId);
328     * </pre>
329     * However, due to a known issue, when logo is persisted, the file status is not consistent
330     * between openInputStream and openOutputStream. So as a workaround, a wait period is applied
331     * to make sure that the logo file is written into the disk.
332     */
333    private PreviewChannel getPreviewChannel(PreviewChannelHelper helper,
334            long channelId) {
335        boolean logoReady = false;
336        PreviewChannel channel = null;
337        while (!logoReady) {
338            try {
339                Thread.sleep(50);
340            } catch (InterruptedException e) {
341            }
342            channel = helper.getPreviewChannel(channelId);
343            logoReady = null != channel.getLogo(mContext);
344        }
345        return channel;
346    }
347
348    /**
349     * Test CR of CRUD
350     * Test that all published preview channels can be read at once.
351     */
352    @Test
353    public void testAllPublishedChannelsRead() throws IOException {
354        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
355            return;
356        }
357        PreviewChannelHelper helper = new PreviewChannelHelper(mContext);
358        PreviewChannel.Builder builder = createFullyPopulatedPreviewChannel();
359        builder.setInternalProviderId("1");
360        helper.publishChannel(builder.build());
361        builder.setInternalProviderId("11");
362        helper.publishChannel(builder.build());
363        builder.setInternalProviderId("111");
364        helper.publishChannel(builder.build());
365        builder.setInternalProviderId("1111");
366        helper.publishChannel(builder.build());
367        List<PreviewChannel> allChannels = helper.getAllChannels();
368        assertEquals(4, allChannels.size());
369    }
370
371    /**
372     * Test UR of CRUD
373     * Test that the PreviewChannelHelper can correctly update and read preview channels.
374     */
375    @Test
376    public void testPreviewChannelUpdate() throws IOException {
377        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
378            return;
379        }
380        PreviewChannelHelper helper = new PreviewChannelHelper(mContext);
381        PreviewChannel.Builder builder = createFullyPopulatedPreviewChannel();
382        long channelId = helper.publishChannel(builder.build());
383        PreviewChannel channelFromTvProvider = getPreviewChannel(helper, channelId);
384        PreviewChannel channel = builder.build();
385        assertTrue(channelsEqual(channel, channelFromTvProvider));
386
387        PreviewChannel patch = new PreviewChannel.Builder()
388                .setDisplayName(channel.getDisplayName())
389                .setAppLinkIntentUri(channel.getAppLinkIntentUri())
390                .setDescription("Patch description").build();
391        helper.updatePreviewChannel(channelId, patch);
392        channelFromTvProvider = helper.getPreviewChannel(channelId);
393        assertFalse(channelsEqual(channel, channelFromTvProvider));
394        assertEquals(channelFromTvProvider.getDescription(), "Patch description");
395    }
396
397    /**
398     * Tests that data is not being updated unnecessarily
399     */
400    @Test
401    public void testDefensiveUpdatePreviewChannel() throws IOException {
402        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
403            return;
404        }
405        final int[] channelUpdateCount = {0};
406        PreviewChannelHelper helper = new PreviewChannelHelper(mContext) {
407            @Override
408            protected void updatePreviewChannelInternal(long channelId, PreviewChannel channel) {
409                channelUpdateCount[0]++;
410            }
411        };
412        PreviewChannel.Builder builder = createFullyPopulatedPreviewChannel();
413        long channelId = helper.publishChannel(builder.build());
414        PreviewChannel fromProvider = helper.getPreviewChannel(channelId);
415        channelsEqual(builder.build(), fromProvider);
416        helper.updatePreviewChannel(channelId, builder.build());
417        assertEquals(0, channelUpdateCount[0]);
418
419        final Uri uri = Uri.parse(new Intent(Intent.ACTION_VIEW).toUri(Intent.URI_INTENT_SCHEME));
420        PreviewChannel channel = new PreviewChannel.Builder()
421                .setDisplayName("Test Display Name Udpate")
422                .setDescription("Test Preview Channel Description")
423                .setAppLinkIntentUri(uri).build();
424
425        helper.updatePreviewChannel(channelId, channel);
426        assertEquals(1, channelUpdateCount[0]);
427    }
428
429    @Test
430    public void testPreviewResolverChannelDeletion() throws IOException {
431        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
432            return;
433        }
434        PreviewChannelHelper helper = new PreviewChannelHelper(mContext);
435        PreviewChannel.Builder builder = createFullyPopulatedPreviewChannel();
436        long channelId = helper.publishChannel(builder.build());
437        PreviewChannel channelFromTvProvider = getPreviewChannel(helper, channelId);
438        assertTrue(channelsEqual(builder.build(), channelFromTvProvider));
439
440        helper.deletePreviewChannel(channelId);
441        channelFromTvProvider = helper.getPreviewChannel(channelId);
442        assertNull(channelFromTvProvider);
443    }
444
445    @Test
446    public void testPreviewProgramCreation() throws IOException {
447        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
448            return;
449        }
450        PreviewChannelHelper helper = new PreviewChannelHelper(mContext);
451        PreviewChannel.Builder channelBuilder = createFullyPopulatedPreviewChannel();
452        long channelId = helper.publishChannel(channelBuilder.build());
453        PreviewProgram program = createFullyPopulatedPreviewProgram(channelId).build();
454        long programId = helper.publishPreviewProgram(program);
455        PreviewProgram programFromProvider = helper.getPreviewProgram(programId);
456        compareProgram(program, programFromProvider);
457    }
458
459    @Test
460    public void testPreviewProgramUpdate() throws IOException {
461        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
462            return;
463        }
464        PreviewChannelHelper helper = new PreviewChannelHelper(mContext);
465        PreviewChannel.Builder channelBuilder = createFullyPopulatedPreviewChannel();
466        long channelId = helper.publishChannel(channelBuilder.build());
467        PreviewProgram.Builder programBuilder = createFullyPopulatedPreviewProgram(channelId);
468        long programId = helper.publishPreviewProgram(programBuilder.build());
469
470        programBuilder.setReleaseDate("2000");
471
472        helper.updatePreviewProgram(programId, programBuilder.build());
473        PreviewProgram programFromProvider = helper.getPreviewProgram(programId);
474        compareProgram(programBuilder.build(), programFromProvider);
475    }
476
477    /**
478     * Tests that data is not being updated unnecessarily
479     */
480    @Test
481    public void testDefensivePreviewProgramUpdateRequests() throws IOException {
482        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
483            return;
484        }
485        final int[] programUpdateCount = {0};
486        PreviewChannelHelper helper = new PreviewChannelHelper(mContext) {
487
488            @Override
489            public void updatePreviewProgramInternal(long programId, PreviewProgram upgrade) {
490                programUpdateCount[0]++;
491            }
492        };
493        PreviewChannel.Builder channelBuilder = createFullyPopulatedPreviewChannel();
494        long channelId = helper.publishChannel(channelBuilder.build());
495        PreviewProgram.Builder programBuilder = createFullyPopulatedPreviewProgram(channelId);
496        long programId = helper.publishPreviewProgram(programBuilder.build());
497        PreviewProgram programFromProvider = helper.getPreviewProgram(programId);
498        compareProgram(programBuilder.build(), programFromProvider);
499        helper.updatePreviewProgram(programId, programBuilder.build());
500        assertEquals(0, programUpdateCount[0]);
501        programBuilder.setDurationMillis(61 * 1000);
502        helper.updatePreviewProgram(programId, programBuilder.build());
503        assertEquals(1, programUpdateCount[0]);
504    }
505
506    @Test
507    public void testDeletePreviewProgram() throws IOException {
508        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
509            return;
510        }
511        PreviewChannelHelper helper = new PreviewChannelHelper(mContext);
512        PreviewChannel.Builder channelBuilder = createFullyPopulatedPreviewChannel();
513        long channelId = helper.publishChannel(channelBuilder.build());
514        PreviewProgram.Builder programBuilder = createFullyPopulatedPreviewProgram(channelId);
515        long programId = helper.publishPreviewProgram(programBuilder.build());
516
517        helper.deletePreviewProgram(programId);
518        PreviewProgram programFromProvider = helper.getPreviewProgram(programId);
519        assertNull(programFromProvider);
520    }
521
522    @Test
523    public void testWatchNextProgramCreation() {
524        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
525            return;
526        }
527        PreviewChannelHelper helper = new PreviewChannelHelper(mContext);
528        WatchNextProgram program = createFullyPopulatedWatchNextProgram().build();
529        long programId = helper.publishWatchNextProgram(program);
530        WatchNextProgram programFromProvider = helper.getWatchNextProgram(programId);
531        compareProgram(program, programFromProvider);
532    }
533
534    @Test
535    public void testUpdateWatchNextProgram() {
536        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
537            return;
538        }
539        PreviewChannelHelper helper = new PreviewChannelHelper(mContext);
540        WatchNextProgram.Builder builder = createFullyPopulatedWatchNextProgram();
541        long programId = helper.publishWatchNextProgram(builder.build());
542        builder.setOfferPrice("10.99 USD");
543        helper.updateWatchNextProgram(builder.build(), programId);
544
545        WatchNextProgram fromProvider = helper.getWatchNextProgram(programId);
546        compareProgram(builder.build(), fromProvider);
547    }
548
549    /**
550     * Tests that data is not being updated unnecessarily
551     */
552    @Test
553    public void testDefensiveUpdateWatchNextProgram() {
554        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
555            return;
556        }
557        final int[] programUpdateCount = {0};
558        PreviewChannelHelper helper = new PreviewChannelHelper(mContext) {
559            @Override
560            protected void updateWatchNextProgram(long programId, WatchNextProgram upgrade) {
561                programUpdateCount[0]++;
562            }
563        };
564        WatchNextProgram.Builder builder = createFullyPopulatedWatchNextProgram();
565        long programId = helper.publishWatchNextProgram(builder.build());
566        WatchNextProgram fromProvider = helper.getWatchNextProgram(programId);
567        compareProgram(builder.build(), fromProvider);
568        helper.updateWatchNextProgram(builder.build(), programId);
569        assertEquals(0, programUpdateCount[0]);
570        builder.setReleaseDate("2000");
571        helper.updateWatchNextProgram(builder.build(), programId);
572        assertEquals(1, programUpdateCount[0]);
573    }
574
575    private boolean channelsEqual(PreviewChannel channelA, PreviewChannel channelB) {
576        boolean result = channelA.getDisplayName().equals(channelB.getDisplayName())
577                && channelA.getType().equals(channelB.getType())
578                && channelA.getAppLinkIntentUri().equals(channelB.getAppLinkIntentUri())
579                && channelA.getDescription().equals(channelB.getDescription())
580                && channelA.getPackageName().equals(channelB.getPackageName())
581                && channelA.getInternalProviderFlag1() == channelB.getInternalProviderFlag1()
582                && channelA.getInternalProviderFlag2() == channelB.getInternalProviderFlag2()
583                && channelA.getInternalProviderFlag3() == channelB.getInternalProviderFlag3()
584                && channelA.getInternalProviderFlag4() == channelB.getInternalProviderFlag4()
585                && (channelA.getInternalProviderId() == null
586                && channelB.getInternalProviderId() == null
587                || channelA.getInternalProviderId().equals(channelB.getInternalProviderId()))
588                && (null != channelA.getLogo(mContext) && null != channelB.getLogo(
589                mContext))
590                && Arrays.equals(channelA.getInternalProviderDataByteArray(),
591                channelB.getInternalProviderDataByteArray());
592        return result;
593    }
594
595    public PreviewChannel.Builder createFullyPopulatedPreviewChannel() {
596        Bitmap logo = BitmapFactory.decodeResource(mContext.getResources(),
597                androidx.tvprovider.test.R.drawable.test_icon);
598        assertNotNull(logo);
599        return new PreviewChannel.Builder()
600                .setAppLinkIntent(new Intent())
601                .setDescription("Test Preview Channel Description")
602                .setDisplayName("Test Display Name")
603                .setPackageName("androidx.tvprovider.media.tv.test")
604                .setInternalProviderFlag1(0x1)
605                .setInternalProviderFlag2(0x2)
606                .setInternalProviderFlag3(0x3)
607                .setInternalProviderFlag4(0x4)
608                .setInternalProviderId("Test Internal provider id")
609                .setInternalProviderData("Test byte array".getBytes())
610                .setLogo(logo);
611    }
612}
613