1 /* 2 * Copyright (C) 2022 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.systemui.clipboardoverlay; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertTrue; 22 import static org.mockito.ArgumentMatchers.anyInt; 23 import static org.mockito.ArgumentMatchers.anyString; 24 import static org.mockito.ArgumentMatchers.eq; 25 import static org.mockito.ArgumentMatchers.isNull; 26 import static org.mockito.Mockito.when; 27 28 import android.app.RemoteAction; 29 import android.content.ClipData; 30 import android.content.ClipDescription; 31 import android.os.PersistableBundle; 32 import android.testing.TestableResources; 33 import android.util.ArrayMap; 34 import android.view.textclassifier.TextClassification; 35 import android.view.textclassifier.TextClassificationManager; 36 import android.view.textclassifier.TextClassifier; 37 import android.view.textclassifier.TextLinks; 38 39 import androidx.test.filters.SmallTest; 40 import androidx.test.runner.AndroidJUnit4; 41 42 import com.android.systemui.R; 43 import com.android.systemui.SysuiTestCase; 44 45 import com.google.android.collect.Lists; 46 47 import org.junit.Before; 48 import org.junit.Test; 49 import org.junit.runner.RunWith; 50 import org.mockito.Answers; 51 import org.mockito.Mock; 52 import org.mockito.Mockito; 53 import org.mockito.MockitoAnnotations; 54 55 import java.util.Map; 56 import java.util.Optional; 57 58 @SmallTest 59 @RunWith(AndroidJUnit4.class) 60 public class ClipboardOverlayUtilsTest extends SysuiTestCase { 61 62 private ClipboardOverlayUtils mClipboardUtils; 63 @Mock 64 private TextClassificationManager mTextClassificationManager; 65 @Mock 66 private TextClassifier mTextClassifier; 67 68 @Mock 69 private ClipData.Item mClipDataItem; 70 71 @Before setUp()72 public void setUp() { 73 MockitoAnnotations.initMocks(this); 74 75 when(mTextClassificationManager.getTextClassifier()).thenReturn(mTextClassifier); 76 mClipboardUtils = new ClipboardOverlayUtils(mTextClassificationManager); 77 } 78 79 @Test test_getAction_noLinks_returnsEmptyOptional()80 public void test_getAction_noLinks_returnsEmptyOptional() { 81 Optional<RemoteAction> action = 82 mClipboardUtils.getAction(Mockito.mock(TextLinks.class), "abc"); 83 84 assertTrue(action.isEmpty()); 85 } 86 87 @Test test_getAction_returnsFirstLink()88 public void test_getAction_returnsFirstLink() { 89 TextLinks links = getFakeTextLinksBuilder().build(); 90 RemoteAction actionA = constructRemoteAction("abc"); 91 RemoteAction actionB = constructRemoteAction("def"); 92 TextClassification classificationA = Mockito.mock(TextClassification.class); 93 when(classificationA.getActions()).thenReturn(Lists.newArrayList(actionA)); 94 TextClassification classificationB = Mockito.mock(TextClassification.class); 95 when(classificationB.getActions()).thenReturn(Lists.newArrayList(actionB)); 96 when(mTextClassifier.classifyText(anyString(), anyInt(), anyInt(), isNull())).thenReturn( 97 classificationA, classificationB); 98 99 RemoteAction result = mClipboardUtils.getAction(links, "test").orElse(null); 100 101 assertEquals(actionA, result); 102 } 103 104 @Test test_getAction_skipsMatchingComponent()105 public void test_getAction_skipsMatchingComponent() { 106 TextLinks links = getFakeTextLinksBuilder().build(); 107 RemoteAction actionA = constructRemoteAction("abc"); 108 RemoteAction actionB = constructRemoteAction("def"); 109 TextClassification classificationA = Mockito.mock(TextClassification.class); 110 when(classificationA.getActions()).thenReturn(Lists.newArrayList(actionA)); 111 TextClassification classificationB = Mockito.mock(TextClassification.class); 112 when(classificationB.getActions()).thenReturn(Lists.newArrayList(actionB)); 113 when(mTextClassifier.classifyText(anyString(), anyInt(), anyInt(), isNull())).thenReturn( 114 classificationA, classificationB); 115 116 RemoteAction result = mClipboardUtils.getAction(links, "abc").orElse(null); 117 118 assertEquals(actionB, result); 119 } 120 121 @Test test_getAction_skipsShortEntity()122 public void test_getAction_skipsShortEntity() { 123 TextLinks.Builder textLinks = new TextLinks.Builder("test text of length 22"); 124 final Map<String, Float> scores = new ArrayMap<>(); 125 scores.put(TextClassifier.TYPE_EMAIL, 1f); 126 textLinks.addLink(20, 22, scores); 127 textLinks.addLink(0, 22, scores); 128 129 RemoteAction actionA = constructRemoteAction("abc"); 130 RemoteAction actionB = constructRemoteAction("def"); 131 TextClassification classificationA = Mockito.mock(TextClassification.class); 132 when(classificationA.getActions()).thenReturn(Lists.newArrayList(actionA)); 133 TextClassification classificationB = Mockito.mock(TextClassification.class); 134 when(classificationB.getActions()).thenReturn(Lists.newArrayList(actionB)); 135 when(mTextClassifier.classifyText(anyString(), eq(20), eq(22), isNull())).thenReturn( 136 classificationA); 137 when(mTextClassifier.classifyText(anyString(), eq(0), eq(22), isNull())).thenReturn( 138 classificationB); 139 140 RemoteAction result = mClipboardUtils.getAction(textLinks.build(), "test").orElse(null); 141 142 assertEquals(actionB, result); 143 } 144 145 @Test test_extra_withPackage_returnsTrue()146 public void test_extra_withPackage_returnsTrue() { 147 PersistableBundle b = new PersistableBundle(); 148 b.putBoolean(ClipDescription.EXTRA_IS_REMOTE_DEVICE, true); 149 ClipData data = constructClipData( 150 new String[]{"text/plain"}, new ClipData.Item("6175550000"), b); 151 TestableResources res = mContext.getOrCreateTestableResources(); 152 res.addOverride( 153 R.string.config_remoteCopyPackage, "com.android.remote/.RemoteActivity"); 154 155 assertTrue(mClipboardUtils.isRemoteCopy(mContext, data, "com.android.remote")); 156 } 157 158 @Test test_noExtra_returnsFalse()159 public void test_noExtra_returnsFalse() { 160 ClipData data = constructClipData( 161 new String[]{"text/plain"}, new ClipData.Item("6175550000"), null); 162 TestableResources res = mContext.getOrCreateTestableResources(); 163 res.addOverride( 164 R.string.config_remoteCopyPackage, "com.android.remote/.RemoteActivity"); 165 166 assertFalse(mClipboardUtils.isRemoteCopy(mContext, data, "com.android.remote")); 167 } 168 169 @Test test_falseExtra_returnsFalse()170 public void test_falseExtra_returnsFalse() { 171 PersistableBundle b = new PersistableBundle(); 172 b.putBoolean(ClipDescription.EXTRA_IS_REMOTE_DEVICE, false); 173 ClipData data = constructClipData( 174 new String[]{"text/plain"}, new ClipData.Item("6175550000"), b); 175 TestableResources res = mContext.getOrCreateTestableResources(); 176 res.addOverride( 177 R.string.config_remoteCopyPackage, "com.android.remote/.RemoteActivity"); 178 179 assertFalse(mClipboardUtils.isRemoteCopy(mContext, data, "com.android.remote")); 180 } 181 182 @Test test_wrongPackage_returnsFalse()183 public void test_wrongPackage_returnsFalse() { 184 PersistableBundle b = new PersistableBundle(); 185 b.putBoolean(ClipDescription.EXTRA_IS_REMOTE_DEVICE, true); 186 ClipData data = constructClipData( 187 new String[]{"text/plain"}, new ClipData.Item("6175550000"), b); 188 189 assertFalse(mClipboardUtils.isRemoteCopy(mContext, data, "")); 190 } 191 constructClipData(String[] mimeTypes, ClipData.Item item, PersistableBundle extras)192 private static ClipData constructClipData(String[] mimeTypes, ClipData.Item item, 193 PersistableBundle extras) { 194 ClipDescription description = new ClipDescription("Test", mimeTypes); 195 if (extras != null) { 196 description.setExtras(extras); 197 } 198 return new ClipData(description, item); 199 } 200 constructRemoteAction(String packageName)201 private static RemoteAction constructRemoteAction(String packageName) { 202 RemoteAction action = Mockito.mock(RemoteAction.class, Answers.RETURNS_DEEP_STUBS); 203 when(action.getActionIntent().getIntent().getComponent().getPackageName()) 204 .thenReturn(packageName); 205 return action; 206 } 207 getFakeTextLinksBuilder()208 private static TextLinks.Builder getFakeTextLinksBuilder() { 209 TextLinks.Builder textLinks = new TextLinks.Builder("test text of length 22"); 210 final Map<String, Float> scores = new ArrayMap<>(); 211 scores.put(TextClassifier.TYPE_EMAIL, 1f); 212 textLinks.addLink(0, 22, scores); 213 textLinks.addLink(0, 22, scores); 214 return textLinks; 215 } 216 } 217