1 /*
2  * Copyright (C) 2015 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;
18 
19 import android.content.ComponentName;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.pm.ActivityInfo;
23 import android.content.pm.PackageManager;
24 import android.support.test.filters.SmallTest;
25 import android.test.AndroidTestCase;
26 
27 import com.android.tv.testing.ComparatorTester;
28 import com.android.tv.util.TvInputManagerHelper;
29 
30 import org.mockito.Matchers;
31 import org.mockito.Mockito;
32 import org.mockito.invocation.InvocationOnMock;
33 import org.mockito.stubbing.Answer;
34 
35 import java.util.Comparator;
36 
37 /**
38  * Tests for {@link Channel}.
39  */
40 @SmallTest
41 public class ChannelTest extends AndroidTestCase {
42     // Used for testing TV inputs with invalid input package. This could happen when a TV input is
43     // uninstalled while drawing an app link card.
44     private static final String INVALID_TV_INPUT_PACKAGE_NAME =
45             "com.android.tv.invalid_tv_input";
46     // Used for testing TV inputs defined inside of Live TV.
47     private static final String LIVE_CHANNELS_PACKAGE_NAME = "com.android.tv";
48     // Used for testing a TV input which doesn't have its leanback launcher activity.
49     private static final String NONE_LEANBACK_TV_INPUT_PACKAGE_NAME =
50             "com.android.tv.none_leanback_tv_input";
51     // Used for testing a TV input which has its leanback launcher activity.
52     private static final String LEANBACK_TV_INPUT_PACKAGE_NAME =
53             "com.android.tv.leanback_tv_input";
54     private static final String TEST_APP_LINK_TEXT = "test_app_link_text";
55     private static final String PARTNER_INPUT_ID = "partner";
56     private static final ActivityInfo TEST_ACTIVITY_INFO = new ActivityInfo();
57 
58     private Context mMockContext;
59     private Intent mInvalidIntent;
60     private Intent mValidIntent;
61 
62     @Override
setUp()63     public void setUp() throws Exception {
64         super.setUp();
65         mInvalidIntent = new Intent(Intent.ACTION_VIEW);
66         mInvalidIntent.setComponent(new ComponentName(INVALID_TV_INPUT_PACKAGE_NAME, ".test"));
67         mValidIntent = new Intent(Intent.ACTION_VIEW);
68         mValidIntent.setComponent(new ComponentName(LEANBACK_TV_INPUT_PACKAGE_NAME, ".test"));
69         Intent liveChannelsIntent = new Intent(Intent.ACTION_VIEW);
70         liveChannelsIntent.setComponent(
71                 new ComponentName(LIVE_CHANNELS_PACKAGE_NAME, ".MainActivity"));
72         Intent leanbackTvInputIntent = new Intent(Intent.ACTION_VIEW);
73         leanbackTvInputIntent.setComponent(
74                 new ComponentName(LEANBACK_TV_INPUT_PACKAGE_NAME, ".test"));
75 
76         PackageManager mockPackageManager = Mockito.mock(PackageManager.class);
77         Mockito.when(mockPackageManager.getLeanbackLaunchIntentForPackage(
78                 INVALID_TV_INPUT_PACKAGE_NAME)).thenReturn(null);
79         Mockito.when(mockPackageManager.getLeanbackLaunchIntentForPackage(
80                 LIVE_CHANNELS_PACKAGE_NAME)).thenReturn(liveChannelsIntent);
81         Mockito.when(mockPackageManager.getLeanbackLaunchIntentForPackage(
82                 NONE_LEANBACK_TV_INPUT_PACKAGE_NAME)).thenReturn(null);
83         Mockito.when(mockPackageManager.getLeanbackLaunchIntentForPackage(
84                 LEANBACK_TV_INPUT_PACKAGE_NAME)).thenReturn(leanbackTvInputIntent);
85 
86         // Channel.getAppLinkIntent() calls initAppLinkTypeAndIntent() which calls
87         // Intent.resolveActivityInfo() which calls PackageManager.getActivityInfo().
88         Mockito.doAnswer(new Answer<ActivityInfo>() {
89             @Override
90             public ActivityInfo answer(InvocationOnMock invocation) {
91                 // We only check the package name, since the class name can be changed
92                 // when an intent is changed to an uri and created from the uri.
93                 // (ex, ".className" -> "packageName.className")
94                 return mValidIntent.getComponent().getPackageName().equals(
95                         ((ComponentName)invocation.getArguments()[0]).getPackageName())
96                         ? TEST_ACTIVITY_INFO : null;
97             }
98         }).when(mockPackageManager).getActivityInfo(Mockito.<ComponentName>any(), Mockito.anyInt());
99 
100         mMockContext = Mockito.mock(Context.class);
101         Mockito.when(mMockContext.getApplicationContext()).thenReturn(mMockContext);
102         Mockito.when(mMockContext.getPackageName()).thenReturn(LIVE_CHANNELS_PACKAGE_NAME);
103         Mockito.when(mMockContext.getPackageManager()).thenReturn(mockPackageManager);
104     }
105 
testGetAppLinkType_NoText_NoIntent()106     public void testGetAppLinkType_NoText_NoIntent() {
107         assertAppLinkType(Channel.APP_LINK_TYPE_NONE, INVALID_TV_INPUT_PACKAGE_NAME, null, null);
108         assertAppLinkType(Channel.APP_LINK_TYPE_NONE, LIVE_CHANNELS_PACKAGE_NAME, null, null);
109         assertAppLinkType(Channel.APP_LINK_TYPE_NONE, NONE_LEANBACK_TV_INPUT_PACKAGE_NAME, null,
110                 null);
111         assertAppLinkType(Channel.APP_LINK_TYPE_APP, LEANBACK_TV_INPUT_PACKAGE_NAME, null, null);
112     }
113 
testGetAppLinkType_NoText_InvalidIntent()114     public void testGetAppLinkType_NoText_InvalidIntent() {
115         assertAppLinkType(Channel.APP_LINK_TYPE_NONE, INVALID_TV_INPUT_PACKAGE_NAME, null,
116                 mInvalidIntent);
117         assertAppLinkType(Channel.APP_LINK_TYPE_NONE, LIVE_CHANNELS_PACKAGE_NAME, null,
118                 mInvalidIntent);
119         assertAppLinkType(Channel.APP_LINK_TYPE_NONE, NONE_LEANBACK_TV_INPUT_PACKAGE_NAME, null,
120                 mInvalidIntent);
121         assertAppLinkType(Channel.APP_LINK_TYPE_APP, LEANBACK_TV_INPUT_PACKAGE_NAME, null,
122                 mInvalidIntent);
123     }
124 
testGetAppLinkType_NoText_ValidIntent()125     public void testGetAppLinkType_NoText_ValidIntent() {
126         assertAppLinkType(Channel.APP_LINK_TYPE_NONE, INVALID_TV_INPUT_PACKAGE_NAME, null,
127                 mValidIntent);
128         assertAppLinkType(Channel.APP_LINK_TYPE_NONE, LIVE_CHANNELS_PACKAGE_NAME, null,
129                 mValidIntent);
130         assertAppLinkType(Channel.APP_LINK_TYPE_NONE, NONE_LEANBACK_TV_INPUT_PACKAGE_NAME, null,
131                 mValidIntent);
132         assertAppLinkType(Channel.APP_LINK_TYPE_APP, LEANBACK_TV_INPUT_PACKAGE_NAME, null,
133                 mValidIntent);
134     }
135 
testGetAppLinkType_HasText_NoIntent()136     public void testGetAppLinkType_HasText_NoIntent() {
137         assertAppLinkType(Channel.APP_LINK_TYPE_NONE, INVALID_TV_INPUT_PACKAGE_NAME,
138                 TEST_APP_LINK_TEXT, null);
139         assertAppLinkType(Channel.APP_LINK_TYPE_NONE, LIVE_CHANNELS_PACKAGE_NAME,
140                 TEST_APP_LINK_TEXT, null);
141         assertAppLinkType(Channel.APP_LINK_TYPE_NONE, NONE_LEANBACK_TV_INPUT_PACKAGE_NAME,
142                 TEST_APP_LINK_TEXT, null);
143         assertAppLinkType(Channel.APP_LINK_TYPE_APP, LEANBACK_TV_INPUT_PACKAGE_NAME,
144                 TEST_APP_LINK_TEXT, null);
145     }
146 
testGetAppLinkType_HasText_InvalidIntent()147     public void testGetAppLinkType_HasText_InvalidIntent() {
148         assertAppLinkType(Channel.APP_LINK_TYPE_NONE, INVALID_TV_INPUT_PACKAGE_NAME,
149                 TEST_APP_LINK_TEXT, mInvalidIntent);
150         assertAppLinkType(Channel.APP_LINK_TYPE_NONE, LIVE_CHANNELS_PACKAGE_NAME,
151                 TEST_APP_LINK_TEXT, mInvalidIntent);
152         assertAppLinkType(Channel.APP_LINK_TYPE_NONE, NONE_LEANBACK_TV_INPUT_PACKAGE_NAME,
153                 TEST_APP_LINK_TEXT, mInvalidIntent);
154         assertAppLinkType(Channel.APP_LINK_TYPE_APP, LEANBACK_TV_INPUT_PACKAGE_NAME,
155                 TEST_APP_LINK_TEXT, mInvalidIntent);
156     }
157 
testGetAppLinkType_HasText_ValidIntent()158     public void testGetAppLinkType_HasText_ValidIntent() {
159         assertAppLinkType(Channel.APP_LINK_TYPE_CHANNEL, INVALID_TV_INPUT_PACKAGE_NAME,
160                 TEST_APP_LINK_TEXT, mValidIntent);
161         assertAppLinkType(Channel.APP_LINK_TYPE_CHANNEL, LIVE_CHANNELS_PACKAGE_NAME,
162                 TEST_APP_LINK_TEXT, mValidIntent);
163         assertAppLinkType(Channel.APP_LINK_TYPE_CHANNEL, NONE_LEANBACK_TV_INPUT_PACKAGE_NAME,
164                 TEST_APP_LINK_TEXT, mValidIntent);
165         assertAppLinkType(Channel.APP_LINK_TYPE_CHANNEL, LEANBACK_TV_INPUT_PACKAGE_NAME,
166                 TEST_APP_LINK_TEXT, mValidIntent);
167     }
168 
assertAppLinkType(int expectedType, String inputPackageName, String appLinkText, Intent appLinkIntent)169     private void assertAppLinkType(int expectedType, String inputPackageName, String appLinkText,
170             Intent appLinkIntent) {
171         // In Channel, Intent.URI_INTENT_SCHEME is used to parse the URI. So the same flag should be
172         // used when the URI is created.
173         Channel testChannel = new Channel.Builder()
174                 .setPackageName(inputPackageName)
175                 .setAppLinkText(appLinkText)
176                 .setAppLinkIntentUri(appLinkIntent == null ? null : appLinkIntent.toUri(
177                         Intent.URI_INTENT_SCHEME))
178                 .build();
179         assertEquals("Unexpected app-link type for for " + testChannel,
180                 expectedType, testChannel.getAppLinkType(mMockContext));
181     }
182 
testComparator()183     public void testComparator() {
184 
185         TvInputManagerHelper manager = Mockito.mock(TvInputManagerHelper.class);
186         Mockito.when(manager.isPartnerInput(Matchers.anyString())).thenAnswer(
187                 new Answer<Boolean>() {
188                     @Override
189                     public Boolean answer(InvocationOnMock invocation) throws Throwable {
190                         String inputId = (String) invocation.getArguments()[0];
191                         return PARTNER_INPUT_ID.equals(inputId);
192                     }
193                 });
194         Comparator<Channel> comparator = new TestChannelComparator(manager);
195         ComparatorTester<Channel> comparatorTester =
196                 ComparatorTester.withoutEqualsTest(comparator);
197         comparatorTester.addComparableGroup(
198                 new Channel.Builder().setInputId(PARTNER_INPUT_ID).build());
199         comparatorTester.addComparableGroup(
200                 new Channel.Builder().setInputId("1").build());
201         comparatorTester.addComparableGroup(
202                 new Channel.Builder().setInputId("1").setDisplayNumber("2").build());
203         comparatorTester.addComparableGroup(
204                 new Channel.Builder().setInputId("2").setDisplayNumber("1.0").build());
205 
206         // display name does not affect comparator
207         comparatorTester.addComparableGroup(
208                 new Channel.Builder().setInputId("2").setDisplayNumber("1.62")
209                         .setDisplayName("test1").build(),
210                 new Channel.Builder().setInputId("2").setDisplayNumber("1.62")
211                         .setDisplayName("test2").build(),
212                 new Channel.Builder().setInputId("2").setDisplayNumber("1.62")
213                         .setDisplayName("test3").build());
214         comparatorTester.addComparableGroup(
215                 new Channel.Builder().setInputId("2").setDisplayNumber("2.0").build());
216         // Numeric display number sorting
217         comparatorTester.addComparableGroup(
218                 new Channel.Builder().setInputId("2").setDisplayNumber("12.2").build());
219         comparatorTester.test();
220     }
221 
222     /**
223      * Test Input Label handled by {@link com.android.tv.data.Channel.DefaultComparator}.
224      *
225      * <p>Sort partner inputs first, then sort by input label, then by input id.
226      * See <a href="http://b/23031603">b/23031603</a>.
227      */
testComparatorLabel()228     public void testComparatorLabel() {
229 
230         TvInputManagerHelper manager = Mockito.mock(TvInputManagerHelper.class);
231         Mockito.when(manager.isPartnerInput(Matchers.anyString())).thenAnswer(
232                 new Answer<Boolean>() {
233                     @Override
234                     public Boolean answer(InvocationOnMock invocation) throws Throwable {
235                         String inputId = (String) invocation.getArguments()[0];
236                         return PARTNER_INPUT_ID.equals(inputId);
237                     }
238                 });
239         Comparator<Channel> comparator = new ChannelComparatorWithDescriptionAsLabel(manager);
240         ComparatorTester<Channel> comparatorTester =
241                 ComparatorTester.withoutEqualsTest(comparator);
242 
243         comparatorTester.addComparableGroup(
244                 new Channel.Builder().setInputId(PARTNER_INPUT_ID).setDescription("A").build());
245 
246         // The description is used as a label for this test.
247         comparatorTester.addComparableGroup(
248                 new Channel.Builder().setDescription("A").setInputId("1").build());
249         comparatorTester.addComparableGroup(
250                 new Channel.Builder().setDescription("A").setInputId("2").build());
251         comparatorTester.addComparableGroup(
252                 new Channel.Builder().setDescription("B").setInputId("1").build());
253 
254         comparatorTester.test();
255     }
256 
257     private class TestChannelComparator extends Channel.DefaultComparator {
TestChannelComparator(TvInputManagerHelper manager)258         public TestChannelComparator(TvInputManagerHelper manager) {
259             super(null, manager);
260         }
261 
262         @Override
getInputLabelForChannel(Channel channel)263         public String getInputLabelForChannel(Channel channel) {
264             return channel.getInputId();
265         }
266     }
267 
268     private static class ChannelComparatorWithDescriptionAsLabel extends Channel.DefaultComparator {
ChannelComparatorWithDescriptionAsLabel(TvInputManagerHelper manager)269         public ChannelComparatorWithDescriptionAsLabel(TvInputManagerHelper manager) {
270             super(null, manager);
271         }
272 
273         @Override
getInputLabelForChannel(Channel channel)274         public String getInputLabelForChannel(Channel channel) {
275             return channel.getDescription();
276         }
277     }
278 }
279