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