| /* |
| * Copyright (C) 2015 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.tv.data; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| import static com.google.common.truth.Truth.assertWithMessage; |
| |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.pm.ActivityInfo; |
| import android.content.pm.PackageManager; |
| import android.content.pm.PackageManager.NameNotFoundException; |
| import android.support.test.filters.SmallTest; |
| import android.support.test.runner.AndroidJUnit4; |
| import com.android.tv.data.api.Channel; |
| import com.android.tv.testing.ComparatorTester; |
| import com.android.tv.util.TvInputManagerHelper; |
| import java.util.Comparator; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.mockito.Matchers; |
| import org.mockito.Mockito; |
| import org.mockito.invocation.InvocationOnMock; |
| import org.mockito.stubbing.Answer; |
| |
| /** Tests for {@link ChannelImpl}. */ |
| @SmallTest |
| @RunWith(AndroidJUnit4.class) |
| public class ChannelImplTest { |
| // Used for testing TV inputs with invalid input package. This could happen when a TV input is |
| // uninstalled while drawing an app link card. |
| private static final String INVALID_TV_INPUT_PACKAGE_NAME = "com.android.tv.invalid_tv_input"; |
| // Used for testing TV inputs defined inside of Live TV. |
| private static final String LIVE_CHANNELS_PACKAGE_NAME = "com.android.tv"; |
| // Used for testing a TV input which doesn't have its leanback launcher activity. |
| private static final String NONE_LEANBACK_TV_INPUT_PACKAGE_NAME = |
| "com.android.tv.none_leanback_tv_input"; |
| // Used for testing a TV input which has its leanback launcher activity. |
| private static final String LEANBACK_TV_INPUT_PACKAGE_NAME = "com.android.tv.leanback_tv_input"; |
| private static final String TEST_APP_LINK_TEXT = "test_app_link_text"; |
| private static final String PARTNER_INPUT_ID = "partner"; |
| private static final ActivityInfo TEST_ACTIVITY_INFO = new ActivityInfo(); |
| |
| private Context mMockContext; |
| private Intent mInvalidIntent; |
| private Intent mValidIntent; |
| |
| @Before |
| public void setUp() throws NameNotFoundException { |
| mInvalidIntent = new Intent(Intent.ACTION_VIEW); |
| mInvalidIntent.setComponent(new ComponentName(INVALID_TV_INPUT_PACKAGE_NAME, ".test")); |
| mValidIntent = new Intent(Intent.ACTION_VIEW); |
| mValidIntent.setComponent(new ComponentName(LEANBACK_TV_INPUT_PACKAGE_NAME, ".test")); |
| Intent liveChannelsIntent = new Intent(Intent.ACTION_VIEW); |
| liveChannelsIntent.setComponent( |
| new ComponentName(LIVE_CHANNELS_PACKAGE_NAME, ".MainActivity")); |
| Intent leanbackTvInputIntent = new Intent(Intent.ACTION_VIEW); |
| leanbackTvInputIntent.setComponent( |
| new ComponentName(LEANBACK_TV_INPUT_PACKAGE_NAME, ".test")); |
| |
| PackageManager mockPackageManager = Mockito.mock(PackageManager.class); |
| Mockito.when( |
| mockPackageManager.getLeanbackLaunchIntentForPackage( |
| INVALID_TV_INPUT_PACKAGE_NAME)) |
| .thenReturn(null); |
| Mockito.when( |
| mockPackageManager.getLeanbackLaunchIntentForPackage( |
| LIVE_CHANNELS_PACKAGE_NAME)) |
| .thenReturn(liveChannelsIntent); |
| Mockito.when( |
| mockPackageManager.getLeanbackLaunchIntentForPackage( |
| NONE_LEANBACK_TV_INPUT_PACKAGE_NAME)) |
| .thenReturn(null); |
| Mockito.when( |
| mockPackageManager.getLeanbackLaunchIntentForPackage( |
| LEANBACK_TV_INPUT_PACKAGE_NAME)) |
| .thenReturn(leanbackTvInputIntent); |
| |
| // Channel.getAppLinkIntent() calls initAppLinkTypeAndIntent() which calls |
| // Intent.resolveActivityInfo() which calls PackageManager.getActivityInfo(). |
| Mockito.doAnswer( |
| new Answer<ActivityInfo>() { |
| @Override |
| public ActivityInfo answer(InvocationOnMock invocation) { |
| // We only check the package name, since the class name can be |
| // changed |
| // when an intent is changed to an uri and created from the uri. |
| // (ex, ".className" -> "packageName.className") |
| return mValidIntent |
| .getComponent() |
| .getPackageName() |
| .equals( |
| ((ComponentName) |
| invocation |
| .getArguments()[0]) |
| .getPackageName()) |
| ? TEST_ACTIVITY_INFO |
| : null; |
| } |
| }) |
| .when(mockPackageManager) |
| .getActivityInfo(Mockito.<ComponentName>any(), Mockito.anyInt()); |
| |
| mMockContext = Mockito.mock(Context.class); |
| Mockito.when(mMockContext.getApplicationContext()).thenReturn(mMockContext); |
| Mockito.when(mMockContext.getPackageName()).thenReturn(LIVE_CHANNELS_PACKAGE_NAME); |
| Mockito.when(mMockContext.getPackageManager()).thenReturn(mockPackageManager); |
| } |
| |
| @Test |
| public void testGetAppLinkType_NoText_NoIntent() { |
| assertAppLinkType(Channel.APP_LINK_TYPE_NONE, INVALID_TV_INPUT_PACKAGE_NAME, null, null); |
| assertAppLinkType(Channel.APP_LINK_TYPE_NONE, LIVE_CHANNELS_PACKAGE_NAME, null, null); |
| assertAppLinkType( |
| Channel.APP_LINK_TYPE_NONE, NONE_LEANBACK_TV_INPUT_PACKAGE_NAME, null, null); |
| assertAppLinkType(Channel.APP_LINK_TYPE_APP, LEANBACK_TV_INPUT_PACKAGE_NAME, null, null); |
| } |
| |
| @Test |
| public void testGetAppLinkType_NoText_InvalidIntent() { |
| assertAppLinkType( |
| Channel.APP_LINK_TYPE_NONE, INVALID_TV_INPUT_PACKAGE_NAME, null, mInvalidIntent); |
| assertAppLinkType( |
| Channel.APP_LINK_TYPE_NONE, LIVE_CHANNELS_PACKAGE_NAME, null, mInvalidIntent); |
| assertAppLinkType( |
| Channel.APP_LINK_TYPE_NONE, |
| NONE_LEANBACK_TV_INPUT_PACKAGE_NAME, |
| null, |
| mInvalidIntent); |
| assertAppLinkType( |
| Channel.APP_LINK_TYPE_APP, LEANBACK_TV_INPUT_PACKAGE_NAME, null, mInvalidIntent); |
| } |
| |
| @Test |
| public void testGetAppLinkType_NoText_ValidIntent() { |
| assertAppLinkType( |
| Channel.APP_LINK_TYPE_NONE, INVALID_TV_INPUT_PACKAGE_NAME, null, mValidIntent); |
| assertAppLinkType( |
| Channel.APP_LINK_TYPE_NONE, LIVE_CHANNELS_PACKAGE_NAME, null, mValidIntent); |
| assertAppLinkType( |
| Channel.APP_LINK_TYPE_NONE, |
| NONE_LEANBACK_TV_INPUT_PACKAGE_NAME, |
| null, |
| mValidIntent); |
| assertAppLinkType( |
| Channel.APP_LINK_TYPE_APP, LEANBACK_TV_INPUT_PACKAGE_NAME, null, mValidIntent); |
| } |
| |
| @Test |
| public void testGetAppLinkType_HasText_NoIntent() { |
| assertAppLinkType( |
| Channel.APP_LINK_TYPE_NONE, |
| INVALID_TV_INPUT_PACKAGE_NAME, |
| TEST_APP_LINK_TEXT, |
| null); |
| assertAppLinkType( |
| Channel.APP_LINK_TYPE_NONE, LIVE_CHANNELS_PACKAGE_NAME, TEST_APP_LINK_TEXT, null); |
| assertAppLinkType( |
| Channel.APP_LINK_TYPE_NONE, |
| NONE_LEANBACK_TV_INPUT_PACKAGE_NAME, |
| TEST_APP_LINK_TEXT, |
| null); |
| assertAppLinkType( |
| Channel.APP_LINK_TYPE_APP, |
| LEANBACK_TV_INPUT_PACKAGE_NAME, |
| TEST_APP_LINK_TEXT, |
| null); |
| } |
| |
| @Test |
| public void testGetAppLinkType_HasText_InvalidIntent() { |
| assertAppLinkType( |
| Channel.APP_LINK_TYPE_NONE, |
| INVALID_TV_INPUT_PACKAGE_NAME, |
| TEST_APP_LINK_TEXT, |
| mInvalidIntent); |
| assertAppLinkType( |
| Channel.APP_LINK_TYPE_NONE, |
| LIVE_CHANNELS_PACKAGE_NAME, |
| TEST_APP_LINK_TEXT, |
| mInvalidIntent); |
| assertAppLinkType( |
| Channel.APP_LINK_TYPE_NONE, |
| NONE_LEANBACK_TV_INPUT_PACKAGE_NAME, |
| TEST_APP_LINK_TEXT, |
| mInvalidIntent); |
| assertAppLinkType( |
| Channel.APP_LINK_TYPE_APP, |
| LEANBACK_TV_INPUT_PACKAGE_NAME, |
| TEST_APP_LINK_TEXT, |
| mInvalidIntent); |
| } |
| |
| @Test |
| public void testGetAppLinkType_HasText_ValidIntent() { |
| assertAppLinkType( |
| Channel.APP_LINK_TYPE_CHANNEL, |
| INVALID_TV_INPUT_PACKAGE_NAME, |
| TEST_APP_LINK_TEXT, |
| mValidIntent); |
| assertAppLinkType( |
| Channel.APP_LINK_TYPE_CHANNEL, |
| LIVE_CHANNELS_PACKAGE_NAME, |
| TEST_APP_LINK_TEXT, |
| mValidIntent); |
| assertAppLinkType( |
| Channel.APP_LINK_TYPE_CHANNEL, |
| NONE_LEANBACK_TV_INPUT_PACKAGE_NAME, |
| TEST_APP_LINK_TEXT, |
| mValidIntent); |
| assertAppLinkType( |
| Channel.APP_LINK_TYPE_CHANNEL, |
| LEANBACK_TV_INPUT_PACKAGE_NAME, |
| TEST_APP_LINK_TEXT, |
| mValidIntent); |
| } |
| |
| private void assertAppLinkType( |
| int expectedType, String inputPackageName, String appLinkText, Intent appLinkIntent) { |
| // In ChannelImpl, Intent.URI_INTENT_SCHEME is used to parse the URI. So the same flag |
| // should be |
| // used when the URI is created. |
| ChannelImpl testChannel = |
| new ChannelImpl.Builder() |
| .setPackageName(inputPackageName) |
| .setAppLinkText(appLinkText) |
| .setAppLinkIntentUri( |
| appLinkIntent == null |
| ? null |
| : appLinkIntent.toUri(Intent.URI_INTENT_SCHEME)) |
| .build(); |
| assertWithMessage("Unexpected app-link type for for " + testChannel) |
| .that(testChannel.getAppLinkType(mMockContext)) |
| .isEqualTo(expectedType); |
| } |
| |
| @Test |
| public void testComparator() { |
| TvInputManagerHelper manager = Mockito.mock(TvInputManagerHelper.class); |
| Mockito.when(manager.isPartnerInput(Matchers.anyString())) |
| .thenAnswer( |
| new Answer<Boolean>() { |
| @Override |
| public Boolean answer(InvocationOnMock invocation) throws Throwable { |
| String inputId = (String) invocation.getArguments()[0]; |
| return PARTNER_INPUT_ID.equals(inputId); |
| } |
| }); |
| Comparator<Channel> comparator = new TestChannelComparator(manager); |
| ComparatorTester<Channel> comparatorTester = ComparatorTester.withoutEqualsTest(comparator); |
| comparatorTester.addComparableGroup( |
| new ChannelImpl.Builder().setInputId(PARTNER_INPUT_ID).build()); |
| comparatorTester.addComparableGroup(new ChannelImpl.Builder().setInputId("1").build()); |
| comparatorTester.addComparableGroup( |
| new ChannelImpl.Builder().setInputId("1").setDisplayNumber("2").build()); |
| comparatorTester.addComparableGroup( |
| new ChannelImpl.Builder().setInputId("2").setDisplayNumber("1.0").build()); |
| |
| // display name does not affect comparator |
| comparatorTester.addComparableGroup( |
| new ChannelImpl.Builder() |
| .setInputId("2") |
| .setDisplayNumber("1.62") |
| .setDisplayName("test1") |
| .build(), |
| new ChannelImpl.Builder() |
| .setInputId("2") |
| .setDisplayNumber("1.62") |
| .setDisplayName("test2") |
| .build(), |
| new ChannelImpl.Builder() |
| .setInputId("2") |
| .setDisplayNumber("1.62") |
| .setDisplayName("test3") |
| .build()); |
| comparatorTester.addComparableGroup( |
| new ChannelImpl.Builder().setInputId("2").setDisplayNumber("2.0").build()); |
| // Numeric display number sorting |
| comparatorTester.addComparableGroup( |
| new ChannelImpl.Builder().setInputId("2").setDisplayNumber("12.2").build()); |
| comparatorTester.test(); |
| } |
| |
| /** |
| * Test Input Label handled by {@link ChannelImpl.DefaultComparator}. |
| * |
| * <p>Sort partner inputs first, then sort by input label, then by input id. See <a |
| * href="http://b/23031603">b/23031603</a>. |
| */ |
| @Test |
| public void testComparatorLabel() { |
| TvInputManagerHelper manager = Mockito.mock(TvInputManagerHelper.class); |
| Mockito.when(manager.isPartnerInput(Matchers.anyString())) |
| .thenAnswer( |
| new Answer<Boolean>() { |
| @Override |
| public Boolean answer(InvocationOnMock invocation) throws Throwable { |
| String inputId = (String) invocation.getArguments()[0]; |
| return PARTNER_INPUT_ID.equals(inputId); |
| } |
| }); |
| Comparator<Channel> comparator = new ChannelComparatorWithDescriptionAsLabel(manager); |
| ComparatorTester<Channel> comparatorTester = ComparatorTester.withoutEqualsTest(comparator); |
| |
| comparatorTester.addComparableGroup( |
| new ChannelImpl.Builder().setInputId(PARTNER_INPUT_ID).setDescription("A").build()); |
| |
| // The description is used as a label for this test. |
| comparatorTester.addComparableGroup( |
| new ChannelImpl.Builder().setDescription("A").setInputId("1").build()); |
| comparatorTester.addComparableGroup( |
| new ChannelImpl.Builder().setDescription("A").setInputId("2").build()); |
| comparatorTester.addComparableGroup( |
| new ChannelImpl.Builder().setDescription("B").setInputId("1").build()); |
| |
| comparatorTester.test(); |
| } |
| |
| @Test |
| public void testNormalizeChannelNumber() { |
| assertNormalizedDisplayNumber(null, null); |
| assertNormalizedDisplayNumber("", ""); |
| assertNormalizedDisplayNumber("1", "1"); |
| assertNormalizedDisplayNumber("abcde", "abcde"); |
| assertNormalizedDisplayNumber("1-1", "1-1"); |
| assertNormalizedDisplayNumber("1.1", "1-1"); |
| assertNormalizedDisplayNumber("1 1", "1-1"); |
| assertNormalizedDisplayNumber("1\u058a1", "1-1"); |
| assertNormalizedDisplayNumber("1\u05be1", "1-1"); |
| assertNormalizedDisplayNumber("1\u14001", "1-1"); |
| assertNormalizedDisplayNumber("1\u18061", "1-1"); |
| assertNormalizedDisplayNumber("1\u20101", "1-1"); |
| assertNormalizedDisplayNumber("1\u20111", "1-1"); |
| assertNormalizedDisplayNumber("1\u20121", "1-1"); |
| assertNormalizedDisplayNumber("1\u20131", "1-1"); |
| assertNormalizedDisplayNumber("1\u20141", "1-1"); |
| } |
| |
| private void assertNormalizedDisplayNumber(String displayNumber, String normalized) { |
| assertThat(ChannelImpl.normalizeDisplayNumber(displayNumber)).isEqualTo(normalized); |
| } |
| |
| private static final class TestChannelComparator extends ChannelImpl.DefaultComparator { |
| public TestChannelComparator(TvInputManagerHelper manager) { |
| super(null, manager); |
| } |
| |
| @Override |
| public String getInputLabelForChannel(Channel channel) { |
| return channel.getInputId(); |
| } |
| } |
| |
| private static final class ChannelComparatorWithDescriptionAsLabel |
| extends ChannelImpl.DefaultComparator { |
| public ChannelComparatorWithDescriptionAsLabel(TvInputManagerHelper manager) { |
| super(null, manager); |
| } |
| |
| @Override |
| public String getInputLabelForChannel(Channel channel) { |
| return channel.getDescription(); |
| } |
| } |
| } |